summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:04:41 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:04:41 +0000
commit16b4362b082f0f88ca8d80d33dca5a74eceb359f (patch)
tree492019ae2b2cc6f867b39d58efb544b0d50ef1cf
parent5f8763c19f32a812310308a909d5251247b07767 (diff)
parent81c5a30224ecdc4cd15d9623fac7af189ee33834 (diff)
downloadcore-aml_tz2_305400100.tar.gz
Change-Id: Ic9e3172639388980a1fdeb04930a98701545882f
-rw-r--r--Android.bp4
-rw-r--r--METADATA3
-rw-r--r--NOTICE324
-rw-r--r--PREUPLOAD.cfg3
-rw-r--r--TEST_MAPPING24
l---------base/.clang-format (renamed from libcrypto_utils/.clang-format)0
-rw-r--r--base/Android.bp225
-rw-r--r--base/CPPLINT.cfg2
-rw-r--r--base/OWNERS3
-rw-r--r--base/README.md42
-rw-r--r--base/abi_compatibility.cpp77
-rw-r--r--base/chrono_utils.cpp42
-rw-r--r--base/chrono_utils_test.cpp80
-rw-r--r--base/cmsg.cpp173
-rw-r--r--base/cmsg_test.cpp202
-rw-r--r--base/endian_test.cpp69
-rw-r--r--base/errors_test.cpp34
-rw-r--r--base/errors_unix.cpp30
-rw-r--r--base/errors_windows.cpp68
-rw-r--r--base/expected_test.cpp876
-rw-r--r--base/file.cpp522
-rw-r--r--base/file_test.cpp385
-rw-r--r--base/format_benchmark.cpp80
-rw-r--r--base/include/android-base/chrono_utils.h53
-rw-r--r--base/include/android-base/cmsg.h106
-rw-r--r--base/include/android-base/collections.h60
-rw-r--r--base/include/android-base/endian.h98
-rw-r--r--base/include/android-base/errno_restorer.h42
-rw-r--r--base/include/android-base/errors.h43
-rw-r--r--base/include/android-base/expected.h744
-rw-r--r--base/include/android-base/file.h125
-rw-r--r--base/include/android-base/format.h28
-rw-r--r--base/include/android-base/logging.h464
-rw-r--r--base/include/android-base/macros.h150
-rw-r--r--base/include/android-base/mapped_file.h93
-rw-r--r--base/include/android-base/memory.h38
-rw-r--r--base/include/android-base/no_destructor.h94
-rw-r--r--base/include/android-base/off64_t.h22
-rw-r--r--base/include/android-base/parsebool.h58
-rw-r--r--base/include/android-base/parsedouble.h77
-rw-r--r--base/include/android-base/parseint.h136
-rw-r--r--base/include/android-base/parsenetaddress.h35
-rw-r--r--base/include/android-base/process.h60
-rw-r--r--base/include/android-base/properties.h101
-rw-r--r--base/include/android-base/result.h235
-rw-r--r--base/include/android-base/scopeguard.h67
-rw-r--r--base/include/android-base/stringprintf.h40
-rw-r--r--base/include/android-base/strings.h94
-rw-r--r--base/include/android-base/test_utils.h86
-rw-r--r--base/include/android-base/thread_annotations.h144
-rw-r--r--base/include/android-base/threads.h30
-rw-r--r--base/include/android-base/unique_fd.h293
-rw-r--r--base/include/android-base/utf8.h104
-rw-r--r--base/liblog_symbols.cpp89
-rw-r--r--base/liblog_symbols.h44
-rw-r--r--base/logging.cpp585
-rw-r--r--base/logging_splitters.h185
-rw-r--r--base/logging_splitters_test.cpp325
-rw-r--r--base/logging_test.cpp673
-rw-r--r--base/macros_test.cpp30
-rw-r--r--base/mapped_file.cpp128
-rw-r--r--base/mapped_file_test.cpp49
-rw-r--r--base/no_destructor_test.cpp66
-rw-r--r--base/parsebool.cpp34
-rw-r--r--base/parsebool_test.cpp48
-rw-r--r--base/parsedouble_test.cpp67
-rw-r--r--base/parseint_test.cpp203
-rw-r--r--base/parsenetaddress.cpp82
-rw-r--r--base/parsenetaddress_test.cpp127
-rw-r--r--base/process.cpp39
-rw-r--r--base/process_test.cpp35
-rw-r--r--base/properties.cpp258
-rw-r--r--base/properties_test.cpp257
-rw-r--r--base/result_test.cpp422
-rw-r--r--base/scopeguard_test.cpp59
-rw-r--r--base/stringprintf.cpp85
-rw-r--r--base/stringprintf_test.cpp60
-rw-r--r--base/strings.cpp139
-rw-r--r--base/strings_test.cpp356
-rw-r--r--base/test_main.cpp25
-rw-r--r--base/test_utils.cpp75
-rw-r--r--base/test_utils_test.cpp86
-rw-r--r--base/threads.cpp54
-rw-r--r--base/utf8.cpp235
-rw-r--r--base/utf8_test.cpp488
-rw-r--r--bootstat/Android.bp9
-rw-r--r--bootstat/AndroidTest.xml26
-rw-r--r--bootstat/OWNERS2
-rwxr-xr-xbootstat/boot_reason_test.sh2
-rw-r--r--bootstat/bootstat.cpp5
-rw-r--r--cli-test/Android.bp4
-rw-r--r--cli-test/cli-test.cpp15
-rw-r--r--code_coverage/Android.bp4
-rw-r--r--code_coverage/seccomp_policy/code_coverage.arm.policy1
-rw-r--r--code_coverage/seccomp_policy/code_coverage.arm64.policy1
-rw-r--r--code_coverage/seccomp_policy/code_coverage.policy.def3
-rw-r--r--code_coverage/seccomp_policy/code_coverage.x86.policy1
-rw-r--r--code_coverage/seccomp_policy/code_coverage.x86_64.policy1
-rw-r--r--cpio/Android.bp11
-rw-r--r--cpio/mkbootfs.c367
-rw-r--r--debuggerd/Android.bp96
-rw-r--r--debuggerd/client/debuggerd_client.cpp35
-rw-r--r--debuggerd/common/include/dump_type.h6
-rw-r--r--debuggerd/crash_dump.cpp128
-rw-r--r--debuggerd/crash_test.cpp21
-rw-r--r--debuggerd/crasher/Android.bp11
-rw-r--r--debuggerd/crasher/crasher.cpp10
-rw-r--r--debuggerd/crasher/mips/crashglue.S48
-rw-r--r--debuggerd/crasher/mips64/crashglue.S48
-rw-r--r--debuggerd/debuggerd_test.cpp716
-rw-r--r--debuggerd/handler/debuggerd_fallback.cpp37
-rw-r--r--debuggerd/handler/debuggerd_handler.cpp133
-rw-r--r--debuggerd/include/debuggerd/handler.h16
-rw-r--r--debuggerd/libdebuggerd/backtrace.cpp33
-rw-r--r--debuggerd/libdebuggerd/gwp_asan.cpp153
-rw-r--r--debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h9
-rw-r--r--debuggerd/libdebuggerd/include/libdebuggerd/scudo.h49
-rw-r--r--debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h36
-rw-r--r--debuggerd/libdebuggerd/include/libdebuggerd/types.h21
-rw-r--r--debuggerd/libdebuggerd/include/libdebuggerd/utility.h11
-rw-r--r--debuggerd/libdebuggerd/scudo.cpp216
-rw-r--r--debuggerd/libdebuggerd/test/UnwinderMock.h3
-rw-r--r--debuggerd/libdebuggerd/test/dump_memory_test.cpp427
-rw-r--r--debuggerd/libdebuggerd/test/sys/system_properties.h37
-rw-r--r--debuggerd/libdebuggerd/test/tombstone_test.cpp41
-rw-r--r--debuggerd/libdebuggerd/tombstone.cpp275
-rw-r--r--debuggerd/libdebuggerd/tombstone_proto.cpp684
-rw-r--r--debuggerd/libdebuggerd/tombstone_proto_to_text.cpp478
-rw-r--r--debuggerd/libdebuggerd/utility.cpp147
-rw-r--r--debuggerd/pbtombstone.cpp62
-rw-r--r--debuggerd/proto/Android.bp40
-rw-r--r--debuggerd/proto/tombstone.proto207
-rw-r--r--debuggerd/protocol.h15
-rw-r--r--debuggerd/seccomp_policy/crash_dump.arm.policy1
-rw-r--r--debuggerd/seccomp_policy/crash_dump.arm64.policy3
-rw-r--r--debuggerd/seccomp_policy/crash_dump.policy.def6
-rw-r--r--debuggerd/seccomp_policy/crash_dump.x86.policy1
-rw-r--r--debuggerd/seccomp_policy/crash_dump.x86_64.policy1
-rw-r--r--debuggerd/tombstoned/include/tombstoned/tombstoned.h6
-rw-r--r--debuggerd/tombstoned/intercept_manager.cpp20
-rw-r--r--debuggerd/tombstoned/tombstoned.cpp326
-rw-r--r--debuggerd/tombstoned/tombstoned.rc3
-rw-r--r--debuggerd/tombstoned/tombstoned_client.cpp23
-rw-r--r--debuggerd/util.cpp42
-rw-r--r--debuggerd/util.h4
-rw-r--r--deprecated-adf/OWNERS2
-rw-r--r--deprecated-adf/libadf/Android.bp26
-rw-r--r--deprecated-adf/libadf/adf.cpp746
-rw-r--r--deprecated-adf/libadf/include/adf/adf.h295
-rw-r--r--deprecated-adf/libadf/include/video/adf.h209
-rw-r--r--deprecated-adf/libadf/original-kernel-headers/video/adf.h386
-rw-r--r--deprecated-adf/libadf/tests/Android.bp23
-rw-r--r--deprecated-adf/libadf/tests/adf_test.cpp403
-rw-r--r--deprecated-adf/libadfhwc/Android.bp29
-rw-r--r--deprecated-adf/libadfhwc/adfhwc.cpp376
-rw-r--r--deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h140
-rw-r--r--diagnose_usb/Android.bp4
-rw-r--r--diagnose_usb/diagnose_usb.cpp2
-rw-r--r--fastboot/Android.bp94
-rw-r--r--fastboot/Android.mk1
-rw-r--r--fastboot/OWNERS4
-rw-r--r--fastboot/bootimg_utils.cpp36
-rw-r--r--fastboot/bootimg_utils.h7
-rw-r--r--fastboot/constants.h2
-rw-r--r--fastboot/device/commands.cpp232
-rw-r--r--fastboot/device/commands.h2
-rw-r--r--fastboot/device/fastboot_device.cpp21
-rw-r--r--fastboot/device/fastboot_device.h7
-rw-r--r--fastboot/device/flashing.cpp17
-rw-r--r--fastboot/device/usb.cpp4
-rw-r--r--fastboot/device/usb_client.cpp12
-rw-r--r--fastboot/device/utility.cpp14
-rw-r--r--fastboot/device/utility.h6
-rw-r--r--fastboot/device/variables.cpp16
-rw-r--r--fastboot/device/variables.h2
-rw-r--r--fastboot/fastboot.bash2
-rw-r--r--fastboot/fastboot.cpp376
-rw-r--r--fastboot/fastboot.h1
-rw-r--r--fastboot/fastboot_driver.cpp99
-rw-r--r--fastboot/fastboot_driver.h19
-rw-r--r--fastboot/fs.cpp61
-rw-r--r--fastboot/fs.h9
-rw-r--r--fastboot/fuzzy_fastboot/Android.bp9
-rw-r--r--fastboot/fuzzy_fastboot/main.cpp80
-rw-r--r--fastboot/testdata/Android.bp140
-rw-r--r--fastboot/testdata/fastboot_gen_rand.py32
-rw-r--r--fastboot/usb.h2
-rw-r--r--fastboot/usb_linux.cpp9
-rw-r--r--fastboot/usb_osx.cpp1
-rw-r--r--fastboot/usb_windows.cpp1
-rw-r--r--fastboot/vendor_boot_img_utils.cpp422
-rw-r--r--fastboot/vendor_boot_img_utils.h34
-rw-r--r--fastboot/vendor_boot_img_utils_test.cpp429
-rw-r--r--fs_mgr/Android.bp30
-rw-r--r--fs_mgr/OWNERS1
-rw-r--r--fs_mgr/README.overlayfs.md8
-rw-r--r--fs_mgr/TEST_MAPPING8
-rw-r--r--fs_mgr/blockdev.cpp159
-rw-r--r--fs_mgr/blockdev.h21
-rw-r--r--fs_mgr/fs_mgr.cpp350
-rw-r--r--fs_mgr/fs_mgr_boot_config.cpp58
-rw-r--r--fs_mgr/fs_mgr_format.cpp10
-rw-r--r--fs_mgr/fs_mgr_fstab.cpp189
-rw-r--r--fs_mgr/fs_mgr_overlayfs.cpp227
-rw-r--r--fs_mgr/fs_mgr_priv_boot_config.h7
-rw-r--r--fs_mgr/fs_mgr_remount.cpp95
-rw-r--r--fs_mgr/fs_mgr_roots.cpp3
-rw-r--r--fs_mgr/fs_mgr_vendor_overlay.cpp2
-rw-r--r--fs_mgr/include/fs_mgr.h27
-rw-r--r--fs_mgr/include/fs_mgr_overlayfs.h8
-rw-r--r--fs_mgr/include_fstab/fstab/fstab.h10
-rw-r--r--fs_mgr/libdm/Android.bp10
-rw-r--r--fs_mgr/libdm/AndroidTest.xml31
-rw-r--r--fs_mgr/libdm/dm.cpp73
-rw-r--r--fs_mgr/libdm/dm_target.cpp12
-rw-r--r--fs_mgr/libdm/dm_test.cpp62
-rw-r--r--fs_mgr/libdm/include/libdm/dm.h17
-rw-r--r--fs_mgr/libdm/include/libdm/dm_target.h13
-rw-r--r--fs_mgr/libfiemap/Android.bp32
-rw-r--r--fs_mgr/libfiemap/binder.cpp19
-rw-r--r--fs_mgr/libfiemap/fiemap_writer.cpp4
-rw-r--r--fs_mgr/libfiemap/image_manager.cpp91
-rw-r--r--fs_mgr/libfiemap/image_test.cpp48
-rw-r--r--fs_mgr/libfiemap/include/libfiemap/image_manager.h25
-rw-r--r--fs_mgr/libfiemap/passthrough.cpp5
-rw-r--r--fs_mgr/libfiemap/split_fiemap_writer.cpp2
-rw-r--r--fs_mgr/libfiemap/utility.cpp28
-rw-r--r--fs_mgr/libfiemap/utility.h4
-rw-r--r--fs_mgr/libfs_avb/Android.bp28
-rw-r--r--fs_mgr/libfs_avb/TEST_MAPPING12
-rw-r--r--fs_mgr/libfs_avb/avb_ops.cpp36
-rw-r--r--fs_mgr/libfs_avb/avb_util.cpp4
-rw-r--r--fs_mgr/libfs_avb/fs_avb.cpp42
-rw-r--r--fs_mgr/libfs_avb/tests/avb_util_test.cpp12
-rw-r--r--fs_mgr/libfs_avb/tests/basic_test.cpp10
-rw-r--r--fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp1
-rw-r--r--fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp2
-rw-r--r--fs_mgr/libfs_avb/tests/util_test.cpp6
-rw-r--r--fs_mgr/liblp/Android.bp8
-rw-r--r--fs_mgr/liblp/AndroidTest.xml31
-rw-r--r--fs_mgr/liblp/builder.cpp151
-rw-r--r--fs_mgr/liblp/builder_test.cpp134
-rw-r--r--fs_mgr/liblp/device_test.cpp7
-rw-r--r--fs_mgr/liblp/images.cpp8
-rw-r--r--fs_mgr/liblp/include/liblp/builder.h35
-rw-r--r--fs_mgr/liblp/partition_opener.cpp6
-rw-r--r--fs_mgr/liblp/reader.cpp4
-rw-r--r--fs_mgr/liblp/utility.h32
-rw-r--r--fs_mgr/liblp/utility_test.cpp31
-rw-r--r--fs_mgr/liblp/writer.cpp4
-rw-r--r--fs_mgr/libsnapshot/Android.bp405
-rw-r--r--fs_mgr/libsnapshot/PowerTest.md40
-rw-r--r--fs_mgr/libsnapshot/VtsLibsnapshotTest.xml29
-rw-r--r--fs_mgr/libsnapshot/android/snapshot/snapshot.proto91
-rw-r--r--fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto110
-rw-r--r--fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt41
-rw-r--r--fs_mgr/libsnapshot/corpus/launch_device.txt161
-rw-r--r--fs_mgr/libsnapshot/cow_api_test.cpp990
-rw-r--r--fs_mgr/libsnapshot/cow_decompress.cpp264
-rw-r--r--fs_mgr/libsnapshot/cow_decompress.h57
-rw-r--r--fs_mgr/libsnapshot/cow_format.cpp91
-rw-r--r--fs_mgr/libsnapshot/cow_reader.cpp564
-rw-r--r--fs_mgr/libsnapshot/cow_snapuserd_test.cpp1138
-rw-r--r--fs_mgr/libsnapshot/cow_writer.cpp551
-rw-r--r--fs_mgr/libsnapshot/device_info.cpp27
-rw-r--r--fs_mgr/libsnapshot/device_info.h6
-rw-r--r--fs_mgr/libsnapshot/dm_snapshot_internals.h71
-rw-r--r--fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp432
-rwxr-xr-xfs_mgr/libsnapshot/fuzz.sh90
-rw-r--r--fs_mgr/libsnapshot/fuzz_utils.cpp38
-rw-r--r--fs_mgr/libsnapshot/fuzz_utils.h285
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_format.h190
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h171
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h156
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h39
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h66
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h53
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapshot.h389
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h62
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h63
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h113
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h81
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h102
-rw-r--r--fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h14
-rw-r--r--fs_mgr/libsnapshot/inspect_cow.cpp174
-rw-r--r--fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp692
-rw-r--r--fs_mgr/libsnapshot/partition_cow_creator.cpp38
-rw-r--r--fs_mgr/libsnapshot/partition_cow_creator.h10
-rw-r--r--fs_mgr/libsnapshot/partition_cow_creator_test.cpp115
-rw-r--r--fs_mgr/libsnapshot/power_test.cpp559
-rwxr-xr-xfs_mgr/libsnapshot/run_power_test.sh35
-rw-r--r--fs_mgr/libsnapshot/snapshot.cpp1603
-rw-r--r--fs_mgr/libsnapshot/snapshot_fuzz.cpp352
-rw-r--r--fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp513
-rw-r--r--fs_mgr/libsnapshot/snapshot_fuzz_utils.h208
-rw-r--r--fs_mgr/libsnapshot/snapshot_metadata_updater.cpp26
-rw-r--r--fs_mgr/libsnapshot/snapshot_metadata_updater.h1
-rw-r--r--fs_mgr/libsnapshot/snapshot_reader.cpp332
-rw-r--r--fs_mgr/libsnapshot/snapshot_reader.h85
-rw-r--r--fs_mgr/libsnapshot/snapshot_reader_test.cpp167
-rw-r--r--fs_mgr/libsnapshot/snapshot_stats.cpp52
-rw-r--r--fs_mgr/libsnapshot/snapshot_stub.cpp181
-rw-r--r--fs_mgr/libsnapshot/snapshot_test.cpp856
-rw-r--r--fs_mgr/libsnapshot/snapshot_writer.cpp196
-rw-r--r--fs_mgr/libsnapshot/snapshot_writer_test.cpp62
-rw-r--r--fs_mgr/libsnapshot/snapshotctl.cpp13
-rw-r--r--fs_mgr/libsnapshot/snapuserd.cpp769
-rw-r--r--fs_mgr/libsnapshot/snapuserd.h343
-rw-r--r--fs_mgr/libsnapshot/snapuserd.rc7
-rw-r--r--fs_mgr/libsnapshot/snapuserd_client.cpp223
-rw-r--r--fs_mgr/libsnapshot/snapuserd_daemon.cpp132
-rw-r--r--fs_mgr/libsnapshot/snapuserd_daemon.h56
-rw-r--r--fs_mgr/libsnapshot/snapuserd_readahead.cpp463
-rw-r--r--fs_mgr/libsnapshot/snapuserd_server.cpp426
-rw-r--r--fs_mgr/libsnapshot/snapuserd_server.h142
-rw-r--r--fs_mgr/libsnapshot/snapuserd_worker.cpp877
-rw-r--r--fs_mgr/libsnapshot/test_helpers.cpp74
-rw-r--r--fs_mgr/libsnapshot/update_engine/update_metadata.proto85
-rw-r--r--fs_mgr/libsnapshot/utility.cpp15
-rw-r--r--fs_mgr/libsnapshot/utility.h7
-rw-r--r--fs_mgr/libstorage_literals/Android.bp9
-rw-r--r--fs_mgr/libvbmeta/Android.bp13
-rw-r--r--fs_mgr/libvbmeta/data/testkey_rsa2048.pem27
-rw-r--r--fs_mgr/libvbmeta/super_vbmeta_test.cpp2
-rw-r--r--fs_mgr/tests/Android.bp5
-rw-r--r--fs_mgr/tests/AndroidTest.xml3
-rwxr-xr-xfs_mgr/tests/adb-remount-test.sh303
-rw-r--r--fs_mgr/tests/fs_mgr_test.cpp388
-rw-r--r--fs_mgr/tools/Android.bp16
-rw-r--r--fs_mgr/tools/dmctl.cpp29
-rw-r--r--fs_mgr/tools/dmuserd.cpp319
-rw-r--r--gatekeeperd/Android.bp9
-rw-r--r--gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl21
-rw-r--r--gatekeeperd/gatekeeperd.cpp150
-rw-r--r--healthd/Android.bp71
-rw-r--r--healthd/Android.mk36
-rw-r--r--healthd/AnimationParser.cpp4
-rw-r--r--healthd/AnimationParser.h4
-rw-r--r--healthd/BatteryMonitor.cpp15
-rw-r--r--healthd/HealthServiceHealthd.cpp93
-rw-r--r--healthd/TEST_MAPPING7
-rw-r--r--healthd/android.hardware.health@2.0-service.rc2
-rw-r--r--healthd/animation.h20
-rw-r--r--healthd/api/charger_sysprop-current.txt29
-rw-r--r--healthd/healthd_mode_charger.cpp67
-rw-r--r--healthd/healthd_mode_charger.h5
-rw-r--r--healthd/healthd_mode_charger_test.cpp181
-rw-r--r--healthd/include/healthd/BatteryMonitor.h3
-rw-r--r--healthd/manifest_healthd.xml11
-rw-r--r--healthd/testdata/Android.bp24
-rw-r--r--healthd/testdata/empty/ensure_directory_creation.txt1
-rw-r--r--healthd/testdata/legacy/res/images/charger/battery_scale.png0
-rw-r--r--healthd/testdata/legacy/res/values/charger/animation.txt9
-rw-r--r--healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt9
-rw-r--r--healthd/testdata/product/product/etc/res/images/charger/battery_fail.png0
-rw-r--r--healthd/testdata/product/product/etc/res/images/charger/battery_scale.png0
-rw-r--r--healthd/testdata/product/product/etc/res/values/charger/animation.txt9
-rw-r--r--healthd/tests/Android.mk21
-rw-r--r--healthd/tests/AnimationParser_test.cpp (renamed from healthd/AnimationParser_test.cpp)0
l---------include/android/log.h1
l---------include/backtrace1
l---------include/log1
l---------include/private/android_filesystem_capability.h1
l---------include/private/android_filesystem_config.h1
l---------include/private/android_logger.h1
l---------include/private/canned_fs_config.h1
-rw-r--r--include/private/fs_config.h4
l---------include/sysutils1
-rw-r--r--init/Android.bp153
-rw-r--r--init/Android.mk36
-rw-r--r--init/AndroidTest.xml3
-rw-r--r--init/OWNERS2
-rw-r--r--init/README.md88
-rw-r--r--init/README.ueventd.md36
-rw-r--r--init/block_dev_initializer.cpp16
-rw-r--r--init/block_dev_initializer.h5
-rw-r--r--init/builtins.cpp114
-rw-r--r--init/capabilities.cpp85
-rw-r--r--init/devices.cpp28
-rw-r--r--init/devices.h7
-rw-r--r--init/devices_test.cpp33
-rw-r--r--init/firmware_handler.cpp79
-rw-r--r--init/firmware_handler.h8
-rw-r--r--init/firmware_handler_test.cpp19
-rw-r--r--init/first_stage_console.cpp89
-rw-r--r--init/first_stage_console.h4
-rw-r--r--init/first_stage_init.cpp87
-rw-r--r--init/first_stage_init.h1
-rw-r--r--init/first_stage_mount.cpp168
-rw-r--r--init/first_stage_mount.h3
-rw-r--r--init/host_init_stubs.h3
-rw-r--r--init/host_init_verifier.cpp90
-rw-r--r--init/init.cpp78
-rw-r--r--init/init.h2
-rw-r--r--init/init_test.cpp13
-rw-r--r--init/main.cpp3
-rw-r--r--init/mount_handler.cpp6
-rw-r--r--init/mount_namespace.cpp193
-rw-r--r--init/mount_namespace.h9
-rw-r--r--init/parser.cpp2
-rwxr-xr-xinit/perfboot.py6
-rw-r--r--init/property_service.cpp274
-rw-r--r--init/property_service_test.cpp35
-rw-r--r--init/reboot.cpp72
-rw-r--r--init/reboot.h6
-rw-r--r--init/reboot_test.cpp196
-rw-r--r--init/reboot_utils.cpp51
-rw-r--r--init/reboot_utils.h3
-rw-r--r--init/second_stage_resources.h32
-rw-r--r--init/security.cpp73
-rw-r--r--init/security.h1
-rw-r--r--init/selinux.cpp234
-rw-r--r--init/service.cpp66
-rw-r--r--init/service.h6
-rw-r--r--init/service_parser.cpp43
-rw-r--r--init/service_utils.cpp21
-rw-r--r--init/service_utils.h10
-rw-r--r--init/snapuserd_transition.cpp332
-rw-r--r--init/snapuserd_transition.h87
-rw-r--r--init/subcontext.cpp19
-rw-r--r--init/subcontext.h7
-rw-r--r--init/subcontext_test.cpp2
-rw-r--r--init/sysprop/Android.bp9
-rw-r--r--init/sysprop/api/com.android.sysprop.init-latest.txt7
-rw-r--r--init/test_kill_services/Android.bp20
-rw-r--r--init/test_kill_services/AndroidTest.xml43
-rw-r--r--init/test_kill_services/init_kill_services_test.cpp69
-rw-r--r--init/test_service/Android.bp9
-rw-r--r--init/tokenizer_test.cpp2
-rw-r--r--init/uevent_listener.cpp20
-rw-r--r--init/uevent_listener.h10
-rw-r--r--init/ueventd.cpp22
-rw-r--r--init/ueventd_parser.cpp27
-rw-r--r--init/ueventd_parser_test.cpp64
-rw-r--r--init/ueventd_test.cpp6
-rw-r--r--init/util.cpp35
-rw-r--r--init/util.h4
-rw-r--r--init/util_test.cpp3
-rw-r--r--libappfuse/Android.bp4
-rw-r--r--libappfuse/tests/FuseAppLoopTest.cc6
-rw-r--r--libasyncio/Android.bp4
l---------libbacktrace/.clang-format1
-rw-r--r--libbacktrace/Android.bp230
-rw-r--r--libbacktrace/Backtrace.cpp183
-rw-r--r--libbacktrace/BacktraceAsyncSafeLog.h41
-rw-r--r--libbacktrace/BacktraceCurrent.cpp232
-rw-r--r--libbacktrace/BacktraceCurrent.h57
-rw-r--r--libbacktrace/BacktraceLog.h31
-rw-r--r--libbacktrace/BacktraceMap.cpp148
-rw-r--r--libbacktrace/BacktracePtrace.cpp112
-rw-r--r--libbacktrace/BacktracePtrace.h37
-rw-r--r--libbacktrace/BacktraceTest.h78
-rw-r--r--libbacktrace/OWNERS2
-rw-r--r--libbacktrace/ThreadEntry.cpp131
-rw-r--r--libbacktrace/ThreadEntry.h71
-rw-r--r--libbacktrace/UnwindMap.cpp148
-rw-r--r--libbacktrace/UnwindMap.h71
-rw-r--r--libbacktrace/UnwindStack.cpp184
-rw-r--r--libbacktrace/UnwindStack.h56
-rw-r--r--libbacktrace/UnwindStackMap.cpp154
-rw-r--r--libbacktrace/UnwindStackMap.h78
-rw-r--r--libbacktrace/backtrace_benchmarks.cpp166
-rw-r--r--libbacktrace/backtrace_read_benchmarks.cpp197
-rw-r--r--libbacktrace/backtrace_test.cpp1893
-rw-r--r--libbacktrace/backtrace_testlib.cpp141
-rw-r--r--libbacktrace/backtrace_testlib.h36
-rw-r--r--libbacktrace/include/backtrace/Backtrace.h214
-rw-r--r--libbacktrace/include/backtrace/BacktraceMap.h191
-rw-r--r--libbacktrace/include/backtrace/backtrace_constants.h30
-rw-r--r--libbacktrace/testdata/arm/libGLESv2_adreno.sobin0 -> 4234180 bytes
-rw-r--r--libbacktrace/testdata/arm/libandroid_runtime.sobin0 -> 1421884 bytes
-rw-r--r--libbacktrace/testdata/arm/libart.sobin0 -> 5529632 bytes
-rwxr-xr-xlibbacktrace/testdata/arm/libbacktrace_test_arm_exidx.sobin0 -> 13804 bytes
-rwxr-xr-xlibbacktrace/testdata/arm/libbacktrace_test_debug_frame.sobin0 -> 99808 bytes
-rwxr-xr-xlibbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.sobin0 -> 36456 bytes
-rw-r--r--libbacktrace/testdata/arm/offline_testdata105
-rw-r--r--libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno6
-rw-r--r--libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime6
-rw-r--r--libbacktrace/testdata/arm/offline_testdata_for_libart10
-rw-r--r--libbacktrace/testdata/arm64/eglSubDriverAndroid.sobin0 -> 81560 bytes
-rwxr-xr-xlibbacktrace/testdata/arm64/libbacktrace_test_eh_frame.sobin0 -> 12232 bytes
-rw-r--r--libbacktrace/testdata/arm64/libskia.sobin0 -> 8322648 bytes
-rw-r--r--libbacktrace/testdata/arm64/offline_testdata107
-rw-r--r--libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid6
-rw-r--r--libbacktrace/testdata/arm64/offline_testdata_for_libskia6
-rwxr-xr-xlibbacktrace/testdata/x86/libbacktrace_test_debug_frame.sobin0 -> 12907 bytes
-rwxr-xr-xlibbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.sobin0 -> 8572 bytes
-rw-r--r--libbacktrace/testdata/x86/offline_testdata82
-rwxr-xr-xlibbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.sobin0 -> 16752 bytes
-rw-r--r--libbacktrace/testdata/x86_64/offline_testdata93
-rw-r--r--libbinderwrapper/Android.bp4
-rw-r--r--libcrypto_utils/Android.bp6
-rw-r--r--libcrypto_utils/android_pubkey.c169
-rw-r--r--libcrypto_utils/android_pubkey.cpp156
-rw-r--r--libcrypto_utils/tests/Android.bp4
-rw-r--r--libcutils/Android.bp135
-rw-r--r--libcutils/OWNERS5
-rw-r--r--libcutils/ashmem-dev.cpp26
-rw-r--r--libcutils/ashmem_test.cpp5
-rw-r--r--libcutils/fs_config.cpp16
-rw-r--r--libcutils/include/cutils/ashmem.h26
-rw-r--r--libcutils/include/cutils/properties.h29
-rw-r--r--libcutils/include/cutils/threads.h49
-rw-r--r--libcutils/include/cutils/trace.h8
-rw-r--r--libcutils/include/private/android_filesystem_config.h16
-rw-r--r--libcutils/include/private/canned_fs_config.h6
l---------libcutils/include_vndk/cutils/android_filesystem_config.h (renamed from libcutils/include_outside_system/cutils/android_filesystem_config.h)0
l---------libcutils/include_vndk/cutils/android_get_control_file.h (renamed from libcutils/include_outside_system/cutils/android_get_control_file.h)0
l---------libcutils/include_vndk/cutils/android_reboot.h (renamed from libcutils/include_outside_system/cutils/android_reboot.h)0
l---------libcutils/include_vndk/cutils/ashmem.h (renamed from libcutils/include_outside_system/cutils/ashmem.h)0
l---------libcutils/include_vndk/cutils/atomic.h (renamed from libcutils/include_outside_system/cutils/atomic.h)0
l---------libcutils/include_vndk/cutils/bitops.h (renamed from libcutils/include_outside_system/cutils/bitops.h)0
l---------libcutils/include_vndk/cutils/compiler.h (renamed from libcutils/include_outside_system/cutils/compiler.h)0
l---------libcutils/include_vndk/cutils/config_utils.h (renamed from libcutils/include_outside_system/cutils/config_utils.h)0
l---------libcutils/include_vndk/cutils/fs.h (renamed from libcutils/include_outside_system/cutils/fs.h)0
l---------libcutils/include_vndk/cutils/hashmap.h (renamed from libcutils/include_outside_system/cutils/hashmap.h)0
l---------libcutils/include_vndk/cutils/iosched_policy.h (renamed from libcutils/include_outside_system/cutils/iosched_policy.h)0
l---------libcutils/include_vndk/cutils/klog.h (renamed from libcutils/include_outside_system/cutils/klog.h)0
l---------libcutils/include_vndk/cutils/list.h (renamed from libcutils/include_outside_system/cutils/list.h)0
l---------libcutils/include_vndk/cutils/log.h (renamed from libcutils/include_outside_system/cutils/log.h)0
l---------libcutils/include_vndk/cutils/memory.h (renamed from libcutils/include_outside_system/cutils/memory.h)0
l---------libcutils/include_vndk/cutils/misc.h (renamed from libcutils/include_outside_system/cutils/misc.h)0
l---------libcutils/include_vndk/cutils/multiuser.h (renamed from libcutils/include_outside_system/cutils/multiuser.h)0
l---------libcutils/include_vndk/cutils/native_handle.h (renamed from libcutils/include_outside_system/cutils/native_handle.h)0
l---------libcutils/include_vndk/cutils/partition_utils.h (renamed from libcutils/include_outside_system/cutils/partition_utils.h)0
l---------libcutils/include_vndk/cutils/properties.h (renamed from libcutils/include_outside_system/cutils/properties.h)0
l---------libcutils/include_vndk/cutils/qtaguid.h (renamed from libcutils/include_outside_system/cutils/qtaguid.h)0
l---------libcutils/include_vndk/cutils/record_stream.h (renamed from libcutils/include_outside_system/cutils/record_stream.h)0
l---------libcutils/include_vndk/cutils/sched_policy.h (renamed from libcutils/include_outside_system/cutils/sched_policy.h)0
l---------libcutils/include_vndk/cutils/sockets.h (renamed from libcutils/include_outside_system/cutils/sockets.h)0
l---------libcutils/include_vndk/cutils/str_parms.h (renamed from libcutils/include_outside_system/cutils/str_parms.h)0
l---------libcutils/include_vndk/cutils/threads.h (renamed from libcutils/include_outside_system/cutils/threads.h)0
l---------libcutils/include_vndk/cutils/trace.h (renamed from libcutils/include_outside_system/cutils/trace.h)0
l---------libcutils/include_vndk/cutils/uevent.h (renamed from libcutils/include_outside_system/cutils/uevent.h)0
-rw-r--r--libcutils/properties.cpp102
-rw-r--r--libcutils/properties_test.cpp253
-rw-r--r--libcutils/qtaguid.cpp10
-rw-r--r--libcutils/rust/cutils.h4
-rw-r--r--libcutils/threads.cpp78
-rw-r--r--libcutils/trace-dev.cpp4
-rw-r--r--libcutils/trace-dev.inc38
-rw-r--r--libcutils/uevent.cpp2
-rw-r--r--libdiskconfig/Android.bp4
-rw-r--r--libgrallocusage/Android.bp17
-rw-r--r--libgrallocusage/OWNERS5
-rw-r--r--libkeyutils/Android.bp13
-rw-r--r--libkeyutils/keyutils_test.cpp2
-rw-r--r--libkeyutils/mini_keyctl/Android.bp27
-rw-r--r--libkeyutils/mini_keyctl/mini_keyctl.cpp (renamed from mini_keyctl/mini_keyctl.cpp)0
-rw-r--r--libkeyutils/mini_keyctl/mini_keyctl_utils.cpp (renamed from mini_keyctl/mini_keyctl_utils.cpp)0
-rw-r--r--libkeyutils/mini_keyctl/mini_keyctl_utils.h (renamed from mini_keyctl/mini_keyctl_utils.h)0
l---------liblog/.clang-format1
-rw-r--r--liblog/Android.bp154
-rw-r--r--liblog/NOTICE190
-rw-r--r--liblog/OWNERS1
-rw-r--r--liblog/README.md166
-rw-r--r--liblog/README.protocol.md49
-rw-r--r--liblog/event.logtags37
-rw-r--r--liblog/event_tag_map.cpp648
-rw-r--r--liblog/include/android/log.h350
-rw-r--r--liblog/include/log/event_tag_map.h72
-rw-r--r--liblog/include/log/log.h156
-rw-r--r--liblog/include/log/log_event_list.h278
-rw-r--r--liblog/include/log/log_id.h41
-rw-r--r--liblog/include/log/log_main.h380
-rw-r--r--liblog/include/log/log_properties.h20
-rw-r--r--liblog/include/log/log_radio.h141
-rw-r--r--liblog/include/log/log_read.h172
-rw-r--r--liblog/include/log/log_safetynet.h29
-rw-r--r--liblog/include/log/log_system.h139
-rw-r--r--liblog/include/log/log_time.h163
-rw-r--r--liblog/include/log/logprint.h160
-rw-r--r--liblog/include/private/android_logger.h167
l---------liblog/include_vndk/android1
-rw-r--r--liblog/include_vndk/log/log.h25
-rw-r--r--liblog/include_vndk/log/log_event_list.h78
l---------liblog/include_vndk/log/log_id.h1
l---------liblog/include_vndk/log/log_main.h1
l---------liblog/include_vndk/log/log_properties.h1
l---------liblog/include_vndk/log/log_radio.h1
l---------liblog/include_vndk/log/log_read.h1
l---------liblog/include_vndk/log/log_safetynet.h1
l---------liblog/include_vndk/log/log_system.h1
-rw-r--r--liblog/include_vndk/log/log_time.h47
-rw-r--r--liblog/liblog.map.txt96
-rw-r--r--liblog/log_event_list.cpp543
-rw-r--r--liblog/log_event_write.cpp48
-rw-r--r--liblog/log_time.cpp189
-rw-r--r--liblog/logd_reader.cpp371
-rw-r--r--liblog/logd_reader.h31
-rw-r--r--liblog/logd_writer.cpp195
-rw-r--r--liblog/logd_writer.h24
-rw-r--r--liblog/logger.h51
-rw-r--r--liblog/logger_name.cpp72
-rw-r--r--liblog/logger_read.cpp149
-rw-r--r--liblog/logger_write.cpp519
-rw-r--r--liblog/logger_write.h21
-rw-r--r--liblog/logprint.cpp1750
-rw-r--r--liblog/pmsg_reader.cpp471
-rw-r--r--liblog/pmsg_reader.h29
-rw-r--r--liblog/pmsg_writer.cpp247
-rw-r--r--liblog/pmsg_writer.h24
-rw-r--r--liblog/properties.cpp668
-rw-r--r--liblog/tests/Android.bp113
-rw-r--r--liblog/tests/AndroidTest.xml32
-rw-r--r--liblog/tests/libc_test.cpp53
-rw-r--r--liblog/tests/liblog_benchmark.cpp1027
-rw-r--r--liblog/tests/liblog_default_tag.cpp160
-rw-r--r--liblog/tests/liblog_global_state.cpp249
-rw-r--r--liblog/tests/liblog_host_test.cpp166
-rw-r--r--liblog/tests/liblog_test.cpp2802
-rw-r--r--liblog/tests/log_id_test.cpp102
-rw-r--r--liblog/tests/log_radio_test.cpp119
-rw-r--r--liblog/tests/log_read_test.cpp82
-rw-r--r--liblog/tests/log_system_test.cpp119
-rw-r--r--liblog/tests/log_time_test.cpp31
-rw-r--r--liblog/tests/log_wrap_test.cpp85
-rw-r--r--liblog/tests/logd_writer_test.cpp99
-rw-r--r--liblog/tests/logprint_test.cpp153
-rw-r--r--liblog/uio.h27
-rw-r--r--libmodprobe/Android.bp4
-rw-r--r--libmodprobe/OWNERS4
-rw-r--r--libmodprobe/include/modprobe/modprobe.h6
-rw-r--r--libmodprobe/libmodprobe.cpp41
-rw-r--r--libmodprobe/libmodprobe_ext.cpp6
-rw-r--r--libmodprobe/libmodprobe_test.cpp43
-rw-r--r--libnetutils/Android.bp22
-rw-r--r--libpackagelistparser/Android.bp6
-rw-r--r--libprocessgroup/Android.bp10
-rw-r--r--libprocessgroup/OWNERS1
-rw-r--r--libprocessgroup/cgroup_map.cpp12
-rw-r--r--libprocessgroup/cgrouprc/Android.bp22
-rw-r--r--libprocessgroup/cgrouprc/include/android/cgrouprc.h10
-rw-r--r--libprocessgroup/cgrouprc/libcgrouprc.llndk.txt (renamed from libprocessgroup/cgrouprc/libcgrouprc.map.txt)0
-rw-r--r--libprocessgroup/cgrouprc_format/Android.bp6
-rw-r--r--libprocessgroup/include/processgroup/processgroup.h4
-rw-r--r--libprocessgroup/include/processgroup/sched_policy.h2
-rw-r--r--libprocessgroup/processgroup.cpp142
-rw-r--r--libprocessgroup/profiles/Android.bp58
-rw-r--r--libprocessgroup/profiles/TEST_MAPPING8
-rw-r--r--libprocessgroup/profiles/cgroups.json39
-rw-r--r--libprocessgroup/profiles/cgroups.proto10
-rw-r--r--libprocessgroup/profiles/cgroups.recovery.json13
-rw-r--r--libprocessgroup/profiles/cgroups_28.json11
-rw-r--r--libprocessgroup/profiles/cgroups_29.json11
-rw-r--r--libprocessgroup/profiles/cgroups_30.json12
-rw-r--r--libprocessgroup/profiles/task_profiles.json134
-rw-r--r--libprocessgroup/profiles/task_profiles_28.json135
-rw-r--r--libprocessgroup/profiles/task_profiles_29.json135
-rw-r--r--libprocessgroup/profiles/task_profiles_30.json135
-rw-r--r--libprocessgroup/profiles/vts_processgroup_validate_test.xml29
-rw-r--r--libprocessgroup/sched_policy.cpp20
-rw-r--r--libprocessgroup/setup/Android.bp4
-rw-r--r--libprocessgroup/setup/cgroup_descriptor.h2
-rw-r--r--libprocessgroup/setup/cgroup_map_write.cpp249
-rw-r--r--libprocessgroup/task_profiles.cpp84
-rw-r--r--libprocessgroup/task_profiles.h15
l---------libprocinfo/.clang-format1
-rw-r--r--libprocinfo/Android.bp131
-rw-r--r--libprocinfo/OWNERS1
-rw-r--r--libprocinfo/include/procinfo/process.h124
-rw-r--r--libprocinfo/include/procinfo/process_map.h184
-rw-r--r--libprocinfo/process.cpp135
-rw-r--r--libprocinfo/process_map.cpp97
-rw-r--r--libprocinfo/process_map_benchmark.cpp76
-rw-r--r--libprocinfo/process_map_test.cpp282
-rw-r--r--libprocinfo/process_test.cpp118
-rw-r--r--libprocinfo/testdata/maps2043
-rw-r--r--libqtaguid/Android.bp4
-rw-r--r--libsparse/Android.bp10
-rw-r--r--libsparse/backed_block.cpp12
-rw-r--r--libsparse/backed_block.h10
-rw-r--r--libsparse/include/sparse/sparse.h16
-rw-r--r--libsparse/output_file.cpp91
-rw-r--r--libsparse/output_file.h10
-rw-r--r--libsparse/sparse.cpp8
-rw-r--r--libstats/OWNERS5
-rw-r--r--libstats/pull_lazy/Android.bp48
-rw-r--r--libstats/pull_lazy/TEST_MAPPING7
-rw-r--r--libstats/pull_lazy/libstatspull_lazy.cpp190
-rw-r--r--libstats/pull_lazy/libstatspull_lazy.h19
-rw-r--r--libstats/pull_lazy/libstatspull_lazy_test.xml37
-rw-r--r--libstats/pull_lazy/tests/libstatspull_lazy_test.cpp58
-rw-r--r--libstats/pull_rust/Android.bp59
-rw-r--r--libstats/pull_rust/stats_pull.rs170
-rw-r--r--libstats/pull_rust/statslog.h3
-rw-r--r--libstats/push_compat/Android.bp12
-rw-r--r--libstats/socket_lazy/Android.bp44
-rw-r--r--libstats/socket_lazy/TEST_MAPPING7
-rw-r--r--libstats/socket_lazy/libstatssocket_lazy.cpp201
-rw-r--r--libstats/socket_lazy/libstatssocket_lazy.h19
-rw-r--r--libstats/socket_lazy/libstatssocket_lazy_test.xml37
-rw-r--r--libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp58
-rw-r--r--libsuspend/Android.bp4
-rw-r--r--libsync/Android.bp26
-rw-r--r--libsync/OWNERS4
-rw-r--r--libsync/include/ndk/sync.h4
-rw-r--r--libsystem/Android.bp6
-rw-r--r--libsystem/OWNERS3
-rw-r--r--libsysutils/Android.bp4
-rw-r--r--libsysutils/src/NetlinkEvent.cpp2
-rw-r--r--libsysutils/src/SocketClient.cpp51
l---------libunwindstack/.clang-format1
-rw-r--r--libunwindstack/Android.bp412
-rw-r--r--libunwindstack/AndroidVersions.md116
-rw-r--r--libunwindstack/ArmExidx.cpp862
-rw-r--r--libunwindstack/ArmExidx.h126
-rw-r--r--libunwindstack/AsmGetRegsMips.S80
-rw-r--r--libunwindstack/AsmGetRegsMips64.S80
-rw-r--r--libunwindstack/AsmGetRegsX86.S62
-rw-r--r--libunwindstack/AsmGetRegsX86_64.S62
-rw-r--r--libunwindstack/Check.h34
-rw-r--r--libunwindstack/DexFile.cpp126
-rw-r--r--libunwindstack/DexFile.h70
-rw-r--r--libunwindstack/DexFiles.cpp200
-rw-r--r--libunwindstack/DwarfCfa.cpp738
-rw-r--r--libunwindstack/DwarfCfa.h267
-rw-r--r--libunwindstack/DwarfDebugFrame.h50
-rw-r--r--libunwindstack/DwarfEhFrame.h49
-rw-r--r--libunwindstack/DwarfEhFrameWithHdr.cpp225
-rw-r--r--libunwindstack/DwarfEhFrameWithHdr.h86
-rw-r--r--libunwindstack/DwarfEncoding.h51
-rw-r--r--libunwindstack/DwarfMemory.cpp252
-rw-r--r--libunwindstack/DwarfOp.cpp1941
-rw-r--r--libunwindstack/DwarfOp.h144
-rw-r--r--libunwindstack/DwarfSection.cpp807
-rw-r--r--libunwindstack/Elf.cpp415
-rw-r--r--libunwindstack/ElfInterface.cpp716
-rw-r--r--libunwindstack/ElfInterfaceArm.cpp184
-rw-r--r--libunwindstack/ElfInterfaceArm.h97
-rw-r--r--libunwindstack/Global.cpp99
-rw-r--r--libunwindstack/JitDebug.cpp222
-rw-r--r--libunwindstack/LocalUnwinder.cpp145
-rw-r--r--libunwindstack/Log.cpp57
-rw-r--r--libunwindstack/MapInfo.cpp352
-rw-r--r--libunwindstack/Maps.cpp209
-rw-r--r--libunwindstack/Memory.cpp478
-rw-r--r--libunwindstack/MemoryBuffer.h57
-rw-r--r--libunwindstack/MemoryCache.h50
-rw-r--r--libunwindstack/MemoryFileAtOffset.h47
-rw-r--r--libunwindstack/MemoryLocal.h36
-rw-r--r--libunwindstack/MemoryOffline.h60
-rw-r--r--libunwindstack/MemoryOfflineBuffer.h43
-rw-r--r--libunwindstack/MemoryRange.h66
-rw-r--r--libunwindstack/MemoryRemote.h45
-rw-r--r--libunwindstack/OWNERS1
-rw-r--r--libunwindstack/Regs.cpp132
-rw-r--r--libunwindstack/RegsArm.cpp205
-rw-r--r--libunwindstack/RegsArm64.cpp158
-rw-r--r--libunwindstack/RegsInfo.h68
-rw-r--r--libunwindstack/RegsMips.cpp181
-rw-r--r--libunwindstack/RegsMips64.cpp168
-rw-r--r--libunwindstack/RegsX86.cpp186
-rw-r--r--libunwindstack/RegsX86_64.cpp176
-rw-r--r--libunwindstack/Symbols.cpp137
-rw-r--r--libunwindstack/Symbols.h70
-rw-r--r--libunwindstack/TEST_MAPPING10
-rw-r--r--libunwindstack/Unwinder.cpp398
-rw-r--r--libunwindstack/benchmarks/unwind_benchmarks.cpp145
-rw-r--r--libunwindstack/include/unwindstack/DexFiles.h79
-rw-r--r--libunwindstack/include/unwindstack/DwarfError.h44
-rw-r--r--libunwindstack/include/unwindstack/DwarfLocation.h53
-rw-r--r--libunwindstack/include/unwindstack/DwarfMemory.h76
-rw-r--r--libunwindstack/include/unwindstack/DwarfSection.h174
-rw-r--r--libunwindstack/include/unwindstack/DwarfStructs.h54
-rw-r--r--libunwindstack/include/unwindstack/Elf.h138
-rw-r--r--libunwindstack/include/unwindstack/ElfInterface.h244
-rw-r--r--libunwindstack/include/unwindstack/Error.h43
-rw-r--r--libunwindstack/include/unwindstack/Global.h63
-rw-r--r--libunwindstack/include/unwindstack/JitDebug.h71
-rw-r--r--libunwindstack/include/unwindstack/LocalUnwinder.h86
-rw-r--r--libunwindstack/include/unwindstack/Log.h29
-rw-r--r--libunwindstack/include/unwindstack/MachineArm.h50
-rw-r--r--libunwindstack/include/unwindstack/MachineArm64.h67
-rw-r--r--libunwindstack/include/unwindstack/MachineMips.h66
-rw-r--r--libunwindstack/include/unwindstack/MachineMips64.h66
-rw-r--r--libunwindstack/include/unwindstack/MachineX86.h51
-rw-r--r--libunwindstack/include/unwindstack/MachineX86_64.h52
-rw-r--r--libunwindstack/include/unwindstack/MapInfo.h125
-rw-r--r--libunwindstack/include/unwindstack/Maps.h136
-rw-r--r--libunwindstack/include/unwindstack/Memory.h59
-rw-r--r--libunwindstack/include/unwindstack/Regs.h115
-rw-r--r--libunwindstack/include/unwindstack/RegsArm.h62
-rw-r--r--libunwindstack/include/unwindstack/RegsArm64.h62
-rw-r--r--libunwindstack/include/unwindstack/RegsGetLocal.h97
-rw-r--r--libunwindstack/include/unwindstack/RegsMips.h62
-rw-r--r--libunwindstack/include/unwindstack/RegsMips64.h62
-rw-r--r--libunwindstack/include/unwindstack/RegsX86.h65
-rw-r--r--libunwindstack/include/unwindstack/RegsX86_64.h65
-rw-r--r--libunwindstack/include/unwindstack/UcontextArm.h63
-rw-r--r--libunwindstack/include/unwindstack/UcontextArm64.h69
-rw-r--r--libunwindstack/include/unwindstack/UcontextMips.h62
-rw-r--r--libunwindstack/include/unwindstack/UcontextMips64.h69
-rw-r--r--libunwindstack/include/unwindstack/UcontextX86.h77
-rw-r--r--libunwindstack/include/unwindstack/UcontextX86_64.h82
-rw-r--r--libunwindstack/include/unwindstack/Unwinder.h155
-rw-r--r--libunwindstack/include/unwindstack/UserArm.h40
-rw-r--r--libunwindstack/include/unwindstack/UserArm64.h43
-rw-r--r--libunwindstack/include/unwindstack/UserMips.h45
-rw-r--r--libunwindstack/include/unwindstack/UserMips64.h45
-rw-r--r--libunwindstack/include/unwindstack/UserX86.h56
-rw-r--r--libunwindstack/include/unwindstack/UserX86_64.h66
-rw-r--r--libunwindstack/tests/ArmExidxDecodeTest.cpp1668
-rw-r--r--libunwindstack/tests/ArmExidxExtractTest.cpp341
-rw-r--r--libunwindstack/tests/DexFileData.h45
-rw-r--r--libunwindstack/tests/DexFileTest.cpp248
-rw-r--r--libunwindstack/tests/DexFilesTest.cpp356
-rw-r--r--libunwindstack/tests/DwarfCfaLogTest.cpp779
-rw-r--r--libunwindstack/tests/DwarfCfaTest.cpp968
-rw-r--r--libunwindstack/tests/DwarfDebugFrameTest.cpp888
-rw-r--r--libunwindstack/tests/DwarfEhFrameTest.cpp133
-rw-r--r--libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp557
-rw-r--r--libunwindstack/tests/DwarfMemoryTest.cpp545
-rw-r--r--libunwindstack/tests/DwarfOpLogTest.cpp73
-rw-r--r--libunwindstack/tests/DwarfOpTest.cpp1586
-rw-r--r--libunwindstack/tests/DwarfSectionImplTest.cpp588
-rw-r--r--libunwindstack/tests/DwarfSectionTest.cpp168
-rw-r--r--libunwindstack/tests/ElfCacheTest.cpp263
-rw-r--r--libunwindstack/tests/ElfFake.cpp74
-rw-r--r--libunwindstack/tests/ElfFake.h153
-rw-r--r--libunwindstack/tests/ElfInterfaceArmTest.cpp422
-rw-r--r--libunwindstack/tests/ElfInterfaceTest.cpp1966
-rw-r--r--libunwindstack/tests/ElfTest.cpp502
-rw-r--r--libunwindstack/tests/ElfTestUtils.cpp142
-rw-r--r--libunwindstack/tests/ElfTestUtils.h38
-rw-r--r--libunwindstack/tests/GenGnuDebugdata.cpp112
-rw-r--r--libunwindstack/tests/IsolatedSettings.cpp26
-rw-r--r--libunwindstack/tests/JitDebugTest.cpp414
-rw-r--r--libunwindstack/tests/LocalUnwinderTest.cpp207
-rw-r--r--libunwindstack/tests/LocalUpdatableMapsTest.cpp373
-rw-r--r--libunwindstack/tests/LogFake.cpp105
-rw-r--r--libunwindstack/tests/LogFake.h30
-rw-r--r--libunwindstack/tests/MapInfoCreateMemoryTest.cpp414
-rw-r--r--libunwindstack/tests/MapInfoGetBuildIDTest.cpp198
-rw-r--r--libunwindstack/tests/MapInfoGetElfTest.cpp422
-rw-r--r--libunwindstack/tests/MapInfoGetLoadBiasTest.cpp171
-rw-r--r--libunwindstack/tests/MapInfoTest.cpp75
-rw-r--r--libunwindstack/tests/MapsTest.cpp618
-rw-r--r--libunwindstack/tests/MemoryBufferTest.cpp100
-rw-r--r--libunwindstack/tests/MemoryCacheTest.cpp142
-rw-r--r--libunwindstack/tests/MemoryFake.cpp61
-rw-r--r--libunwindstack/tests/MemoryFake.h84
-rw-r--r--libunwindstack/tests/MemoryFileTest.cpp276
-rw-r--r--libunwindstack/tests/MemoryLocalTest.cpp111
-rw-r--r--libunwindstack/tests/MemoryOfflineBufferTest.cpp96
-rw-r--r--libunwindstack/tests/MemoryOfflineTest.cpp67
-rw-r--r--libunwindstack/tests/MemoryRangeTest.cpp105
-rw-r--r--libunwindstack/tests/MemoryRangesTest.cpp89
-rw-r--r--libunwindstack/tests/MemoryRemoteTest.cpp353
-rw-r--r--libunwindstack/tests/MemoryTest.cpp96
-rw-r--r--libunwindstack/tests/RegsFake.h102
-rw-r--r--libunwindstack/tests/RegsInfoTest.cpp92
-rw-r--r--libunwindstack/tests/RegsIterateTest.cpp257
-rw-r--r--libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp261
-rw-r--r--libunwindstack/tests/RegsTest.cpp324
-rw-r--r--libunwindstack/tests/SymbolsTest.cpp372
-rw-r--r--libunwindstack/tests/TestLocal.cpp39
-rw-r--r--libunwindstack/tests/TestUtils.cpp44
-rw-r--r--libunwindstack/tests/TestUtils.h57
-rw-r--r--libunwindstack/tests/UnwindOfflineTest.cpp1739
-rw-r--r--libunwindstack/tests/UnwindTest.cpp479
-rw-r--r--libunwindstack/tests/UnwinderTest.cpp1477
-rw-r--r--libunwindstack/tests/VerifyBionicTerminationTest.cpp124
-rw-r--r--libunwindstack/tests/files/elf32.xzbin0 -> 140 bytes
-rw-r--r--libunwindstack/tests/files/elf64.xzbin0 -> 144 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.databin0 -> 24 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.databin0 -> 624 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.databin0 -> 612 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.sobin0 -> 6095540 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.sobin0 -> 841140 bytes
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt4
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.databin0 -> 61080 bytes
-rw-r--r--libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.sobin0 -> 1139592 bytes
-rw-r--r--libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt2
-rw-r--r--libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt4
-rw-r--r--libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.databin0 -> 72 bytes
-rw-r--r--libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64bin0 -> 67352 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_first_x86/libc.sobin0 -> 52 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt2
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt9
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_first_x86/stack.databin0 -> 192 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_first_x86/waiterbin0 -> 9816 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.sobin0 -> 655400 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.sobin0 -> 826064 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt3
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserverbin0 -> 20220 bytes
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.databin0 -> 2512 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.sobin0 -> 1105628 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt3
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt9
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.databin0 -> 9688 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstonedbin0 -> 141432 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.sobin0 -> 4096 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.sobin0 -> 64 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt2
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt11
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.databin0 -> 1480 bytes
-rw-r--r--libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64bin0 -> 70288 bytes
-rw-r--r--libunwindstack/tests/files/offline/empty_arm64/libbinder.sobin0 -> 659904 bytes
-rw-r--r--libunwindstack/tests/files/offline/empty_arm64/libc.sobin0 -> 947288 bytes
-rw-r--r--libunwindstack/tests/files/offline/empty_arm64/maps.txt9
-rw-r--r--libunwindstack/tests/files/offline/empty_arm64/netdbin0 -> 665448 bytes
-rw-r--r--libunwindstack/tests/files/offline/empty_arm64/regs.txt34
-rw-r--r--libunwindstack/tests/files/offline/empty_arm64/stack.databin0 -> 5304 bytes
-rw-r--r--libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.sobin0 -> 1421884 bytes
-rw-r--r--libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt1
-rw-r--r--libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt2
-rw-r--r--libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.databin0 -> 24 bytes
-rw-r--r--libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt1
-rw-r--r--libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odexbin0 -> 22616 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32bin0 -> 24484 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/descriptor.databin0 -> 24 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.databin0 -> 24 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry0.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry1.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry2.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry3.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry4.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry5.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/entry6.databin0 -> 32 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit0.databin0 -> 1208 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit1.databin0 -> 1268 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit2.databin0 -> 1368 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit3.databin0 -> 1988 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit4.databin0 -> 1232 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit5.databin0 -> 2400 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/jit6.databin0 -> 1208 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/libart.sobin0 -> 6111860 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/libartd.sobin0 -> 5421248 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.sobin0 -> 195644 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/libc.sobin0 -> 52 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/maps.txt11
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/stack.databin0 -> 8056 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odexbin0 -> 22252 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32bin0 -> 242656 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/descriptor.databin0 -> 24 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry0.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry1.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry2.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry3.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry4.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry5.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/entry6.databin0 -> 28 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit0.databin0 -> 1112 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit1.databin0 -> 1184 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit2.databin0 -> 1288 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit3.databin0 -> 1988 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit4.databin0 -> 1144 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit5.databin0 -> 2356 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/jit6.databin0 -> 1136 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/libartd.sobin0 -> 10629892 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.sobin0 -> 1089016 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/libc.sobin0 -> 52 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/maps.txt8
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/regs.txt9
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/stack.databin0 -> 11496 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/jit_map0.sobin0 -> 616 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/jit_map1.sobin0 -> 604 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/libart.sobin0 -> 6095540 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/libc.sobin0 -> 841140 bytes
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/maps.txt2
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/jit_map_arm/stack.databin0 -> 61080 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.sobin0 -> 1214296 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64bin0 -> 1562976 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt7
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt33
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.databin0 -> 5144 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.databin0 -> 4360 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/testbin0 -> 4699528 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdsobin0 -> 8192 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.sobin0 -> 1820400 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt3
-rw-r--r--libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittestsbin0 -> 51825928 bytes
-rw-r--r--libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt17
-rw-r--r--libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.databin0 -> 11328 bytes
-rw-r--r--libunwindstack/tests/files/offline/offset_arm/libc.sobin0 -> 839184 bytes
-rw-r--r--libunwindstack/tests/files/offline/offset_arm/libunwindstack_testbin0 -> 3784164 bytes
-rw-r--r--libunwindstack/tests/files/offline/offset_arm/maps.txt4
-rw-r--r--libunwindstack/tests/files/offline/offset_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/offset_arm/stack0.databin0 -> 828 bytes
-rw-r--r--libunwindstack/tests/files/offline/offset_arm/stack1.databin0 -> 4392 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apkbin0 -> 1014521 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.sobin0 -> 1240288 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64bin0 -> 2120488 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt7
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt33
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.databin0 -> 5144 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.databin0 -> 5289 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.sobin0 -> 8192 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.databin0 -> 970760 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.sobin0 -> 1240288 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64bin0 -> 2120488 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt7
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt33
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.databin0 -> 5144 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.databin0 -> 5289 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.sobin0 -> 8192 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.sobin0 -> 1241432 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt3
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt33
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.databin0 -> 14392 bytes
-rw-r--r--libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apkbin0 -> 22979479 bytes
-rw-r--r--libunwindstack/tests/files/offline/signal_load_bias_arm/libc.sobin0 -> 1032144 bytes
-rw-r--r--libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_testbin0 -> 3435880 bytes
-rw-r--r--libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt4
-rw-r--r--libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt16
-rw-r--r--libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.databin0 -> 800 bytes
-rw-r--r--libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.databin0 -> 6256 bytes
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm/libbase.sobin0 -> 62552 bytes
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm/libc.sobin0 -> 840504 bytes
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm/maps.txt4
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm/regs.txt3
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm/stack.databin0 -> 232 bytes
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_testbin0 -> 5198416 bytes
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm64/maps.txt2
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm64/regs.txt4
-rw-r--r--libunwindstack/tests/files/offline/straddle_arm64/stack.databin0 -> 232 bytes
-rw-r--r--libunwindstack/tools/unwind.cpp124
-rw-r--r--libunwindstack/tools/unwind_for_offline.cpp358
-rw-r--r--libunwindstack/tools/unwind_info.cpp204
-rw-r--r--libunwindstack/tools/unwind_reg_info.cpp286
-rw-r--r--libunwindstack/tools/unwind_symbols.cpp124
-rw-r--r--libusbhost/Android.bp4
-rw-r--r--libusbhost/usbhost.c5
-rw-r--r--libutils/Android.bp168
-rw-r--r--libutils/BitSet_fuzz.cpp70
-rw-r--r--libutils/CallStack_fuzz.cpp50
-rw-r--r--libutils/FileMap_fuzz.cpp50
-rw-r--r--libutils/FuzzFormatTypes.h61
-rw-r--r--libutils/LightRefBase.cpp29
-rw-r--r--libutils/Looper_fuzz.cpp84
-rw-r--r--libutils/Looper_test.cpp44
-rw-r--r--libutils/Looper_test_pipe.h55
-rw-r--r--libutils/LruCache_fuzz.cpp74
-rwxr-xr-xlibutils/Printer_fuzz.cpp52
-rw-r--r--libutils/ProcessCallStack_fuzz.cpp77
-rw-r--r--libutils/PropertyMap.cpp214
-rw-r--r--libutils/README289
-rw-r--r--libutils/RefBase.cpp71
-rw-r--r--libutils/RefBase_fuzz.cpp205
-rw-r--r--libutils/RefBase_test.cpp24
-rw-r--r--libutils/SharedBuffer_test.cpp90
-rw-r--r--libutils/StopWatch.cpp44
-rw-r--r--libutils/String16.cpp181
-rw-r--r--libutils/String16_fuzz.cpp109
-rw-r--r--libutils/String16_test.cpp78
-rw-r--r--libutils/String8.cpp96
-rw-r--r--libutils/String8_fuzz.cpp233
-rw-r--r--libutils/String8_test.cpp19
-rw-r--r--libutils/StrongPointer_test.cpp63
-rw-r--r--libutils/SystemClock.cpp11
-rw-r--r--libutils/SystemClock_test.cpp20
-rw-r--r--libutils/Timers.cpp40
-rw-r--r--libutils/Timers_test.cpp29
-rw-r--r--libutils/Unicode.cpp92
-rw-r--r--libutils/Vector_fuzz.cpp83
-rw-r--r--libutils/include/utils/BitSet.h45
-rw-r--r--libutils/include/utils/Debug.h26
-rw-r--r--libutils/include/utils/Flattenable.h6
-rw-r--r--libutils/include/utils/KeyedVector.h22
-rw-r--r--libutils/include/utils/LightRefBase.h8
-rw-r--r--libutils/include/utils/Looper.h7
-rw-r--r--libutils/include/utils/PropertyMap.h106
-rw-r--r--libutils/include/utils/RefBase.h137
-rw-r--r--libutils/include/utils/StopWatch.h48
-rw-r--r--libutils/include/utils/String16.h16
-rw-r--r--libutils/include/utils/String8.h20
-rw-r--r--libutils/include/utils/StrongPointer.h127
-rw-r--r--libutils/include/utils/SystemClock.h8
-rw-r--r--libutils/include/utils/Timers.h18
-rw-r--r--libutils/include/utils/Unicode.h23
-rw-r--r--libutils/include/utils/Vector.h8
-rw-r--r--libvndksupport/Android.bp14
-rw-r--r--libvndksupport/tests/Android.bp4
l---------libziparchive/.clang-format1
-rw-r--r--libziparchive/Android.bp218
-rw-r--r--libziparchive/OWNERS1
-rw-r--r--libziparchive/cli-tests/files/example.zipbin0 -> 1082 bytes
-rwxr-xr-xlibziparchive/cli-tests/unzip.test148
-rwxr-xr-xlibziparchive/cli-tests/zipinfo.test53
-rw-r--r--libziparchive/entry_name_utils-inl.h65
-rw-r--r--libziparchive/entry_name_utils_test.cc62
-rw-r--r--libziparchive/include/ziparchive/zip_archive.h289
-rw-r--r--libziparchive/include/ziparchive/zip_archive_stream_entry.h46
-rw-r--r--libziparchive/include/ziparchive/zip_writer.h189
-rw-r--r--libziparchive/libziparchive_fuzzer.cpp13
-rwxr-xr-xlibziparchive/run-ziptool-tests-on-android.sh15
-rw-r--r--libziparchive/testdata/bad_crc.zipbin0 -> 320 bytes
-rw-r--r--libziparchive/testdata/bad_filename.zipbin0 -> 4119 bytes
-rw-r--r--libziparchive/testdata/crash.apkbin0 -> 154 bytes
-rw-r--r--libziparchive/testdata/declaredlength.zipbin0 -> 886 bytes
-rw-r--r--libziparchive/testdata/dummy-update.zipbin0 -> 1090065 bytes
-rw-r--r--libziparchive/testdata/empty.zipbin0 -> 22 bytes
-rw-r--r--libziparchive/testdata/large.zipbin0 -> 315660 bytes
-rw-r--r--libziparchive/testdata/valid.zipbin0 -> 758 bytes
-rw-r--r--libziparchive/testdata/zero-size-cd.zipbin0 -> 152 bytes
-rw-r--r--libziparchive/zip_archive.cc1315
-rw-r--r--libziparchive/zip_archive_benchmark.cpp135
-rw-r--r--libziparchive/zip_archive_common.h183
-rw-r--r--libziparchive/zip_archive_private.h195
-rw-r--r--libziparchive/zip_archive_stream_entry.cc323
-rw-r--r--libziparchive/zip_archive_test.cc859
-rw-r--r--libziparchive/zip_writer.cc582
-rw-r--r--libziparchive/zip_writer_test.cc428
-rw-r--r--libziparchive/ziptool-tests.xml26
-rw-r--r--libziparchive/ziptool.cpp528
-rw-r--r--llkd/Android.bp4
-rw-r--r--llkd/README.md2
-rw-r--r--llkd/include/llkd.h2
-rw-r--r--llkd/libllkd.cpp7
-rw-r--r--llkd/tests/Android.bp4
-rw-r--r--logcat/Android.bp57
-rw-r--r--logcat/MODULE_LICENSE_APACHE2 (renamed from healthd/testdata/legacy/res/images/charger/battery_fail.png)0
-rw-r--r--logcat/NOTICE190
-rw-r--r--logcat/OWNERS1
-rw-r--r--logcat/event.logtags156
-rw-r--r--logcat/logcat.cpp1183
-rwxr-xr-xlogcat/logcatd29
-rw-r--r--logcat/logcatd.rc62
-rwxr-xr-xlogcat/logpersist178
-rw-r--r--logcat/tests/Android.bp57
-rw-r--r--logcat/tests/logcat_benchmark.cpp133
-rw-r--r--logcat/tests/logcat_test.cpp1760
-rw-r--r--logcat/tests/logcatd_test.cpp20
l---------logd/.clang-format1
-rw-r--r--logd/Android.bp105
-rw-r--r--logd/CommandListener.cpp378
-rw-r--r--logd/CommandListener.h98
-rw-r--r--logd/FlushCommand.cpp85
-rw-r--r--logd/FlushCommand.h43
-rw-r--r--logd/LogAudit.cpp402
-rw-r--r--logd/LogAudit.h56
-rw-r--r--logd/LogBuffer.cpp1225
-rw-r--r--logd/LogBuffer.h189
-rw-r--r--logd/LogBufferElement.cpp281
-rw-r--r--logd/LogBufferElement.h99
-rw-r--r--logd/LogCommand.cpp145
-rw-r--r--logd/LogCommand.h30
-rw-r--r--logd/LogKlog.cpp826
-rw-r--r--logd/LogKlog.h64
-rw-r--r--logd/LogListener.cpp131
-rw-r--r--logd/LogListener.h37
-rw-r--r--logd/LogReader.cpp266
-rw-r--r--logd/LogReader.h48
-rw-r--r--logd/LogStatistics.cpp855
-rw-r--r--logd/LogStatistics.h790
-rw-r--r--logd/LogTags.cpp903
-rw-r--r--logd/LogTags.h124
-rw-r--r--logd/LogTimes.cpp243
-rw-r--r--logd/LogTimes.h105
-rw-r--r--logd/LogUtils.h76
-rw-r--r--logd/LogWhiteBlackList.cpp271
-rw-r--r--logd/LogWhiteBlackList.h88
-rw-r--r--logd/OWNERS2
-rw-r--r--logd/README.auditd17
-rw-r--r--logd/README.property76
-rw-r--r--logd/auditctl.cpp74
-rw-r--r--logd/event.logtags39
-rw-r--r--logd/fuzz/Android.bp30
-rw-r--r--logd/fuzz/log_buffer_log_fuzzer.cpp115
-rw-r--r--logd/libaudit.c253
-rw-r--r--logd/libaudit.h106
-rw-r--r--logd/logd.rc35
-rw-r--r--logd/logtagd.rc9
-rw-r--r--logd/main.cpp439
-rw-r--r--logd/tests/Android.bp68
-rw-r--r--logd/tests/AndroidTest.xml32
-rw-r--r--logd/tests/logd_test.cpp959
-rw-r--r--logwrapper/Android.bp70
-rw-r--r--logwrapper/NOTICE190
-rw-r--r--logwrapper/OWNERS1
-rw-r--r--logwrapper/include/logwrap/logwrap.h61
-rw-r--r--logwrapper/logwrap.cpp615
-rw-r--r--logwrapper/logwrap_fork_execvp_benchmark.cpp32
-rw-r--r--logwrapper/logwrapper.cpp95
-rw-r--r--mini_keyctl/Android.bp31
-rw-r--r--mkbootfs/Android.bp19
-rw-r--r--mkbootfs/mkbootfs.c368
-rw-r--r--property_service/OWNERS2
-rw-r--r--property_service/libpropertyinfoparser/Android.bp6
-rw-r--r--property_service/libpropertyinfoserializer/Android.bp5
-rw-r--r--property_service/libpropertyinfoserializer/property_info_serializer_test.cpp2
-rw-r--r--property_service/property_info_checker/Android.bp5
-rw-r--r--qemu_pipe/Android.bp20
-rw-r--r--qemu_pipe/OWNERS1
-rw-r--r--qemu_pipe/include/qemu_pipe.h64
-rw-r--r--qemu_pipe/qemu_pipe.cpp108
-rw-r--r--reboot/Android.bp4
-rw-r--r--rootdir/Android.bp24
-rw-r--r--rootdir/Android.mk46
-rw-r--r--rootdir/OWNERS4
-rw-r--r--rootdir/avb/Android.bp11
-rw-r--r--rootdir/avb/Android.mk77
-rw-r--r--rootdir/avb/qcar-gsi.avbpubkeybin1032 -> 0 bytes
-rw-r--r--rootdir/etc/linker.config.json30
-rw-r--r--rootdir/etc/public.libraries.android.txt2
-rw-r--r--rootdir/etc/public.libraries.iot.txt1
-rw-r--r--rootdir/etc/public.libraries.wear.txt1
-rw-r--r--rootdir/init-debug.rc8
-rw-r--r--rootdir/init.environ.rc.in3
-rw-r--r--rootdir/init.rc302
-rw-r--r--rootdir/init.usb.rc3
-rw-r--r--rootdir/init.zygote32.rc1
-rw-r--r--rootdir/init.zygote32_64.rc25
-rw-r--r--rootdir/init.zygote64.rc1
-rw-r--r--rootdir/init.zygote64_32.rc1
-rw-r--r--rootdir/ueventd.rc17
-rw-r--r--run-as/Android.bp18
-rw-r--r--run-as/run-as.cpp4
-rw-r--r--sdcard/Android.bp4
-rw-r--r--set-verity-state/Android.bp4
-rw-r--r--shell_and_utilities/Android.bp16
-rw-r--r--shell_and_utilities/README.md131
-rw-r--r--storaged/Android.bp4
-rw-r--r--toolbox/Android.bp18
-rw-r--r--toolbox/OWNERS3
-rw-r--r--toolbox/modprobe.cpp176
-rw-r--r--toolbox/start.cpp3
-rw-r--r--trusty/Android.bp42
-rw-r--r--trusty/OWNERS6
-rw-r--r--trusty/apploader/Android.bp40
-rw-r--r--trusty/apploader/apploader.cpp272
-rw-r--r--trusty/apploader/apploader_ipc.h92
-rw-r--r--trusty/apploader/fuzz/Android.bp40
-rw-r--r--trusty/apploader/fuzz/app_fuzzer.cpp134
-rw-r--r--trusty/confirmationui/Android.bp7
-rw-r--r--trusty/confirmationui/NotSoSecureInput.cpp2
-rw-r--r--trusty/confirmationui/TrustyApp.cpp237
-rw-r--r--trusty/confirmationui/TrustyApp.h33
-rw-r--r--trusty/confirmationui/TrustyConfirmationUI.cpp4
-rw-r--r--trusty/confirmationui/TrustyConfirmationUI.h2
-rw-r--r--trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc4
-rw-r--r--trusty/confirmationui/fuzz/Android.bp43
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIlbin48 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOddbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a00
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumRbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64bbin96 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCKbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ00
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPibin24 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Kobin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABYbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUqbin48 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vffbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTSTbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055eibin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEmbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8bin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbbbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7tbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzubin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5bin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rPbin12 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj0
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHabin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWzbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4Ubin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hTbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vGbin6192 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpXbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWzbin140 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKCbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFnbin64 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSSbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30bin136 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnCbin64 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNnbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZibin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofvbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAGbin64 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbnbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzsbin40 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBCbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZpbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfEbin144 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6bin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1rbin88 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0bin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LLbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXsbin24 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5angbin24 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedbbin40 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUtbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14bin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTwbin88 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJCbin148 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5bin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDHbin48 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRCbin8 -> 0 bytes
-rw-r--r--trusty/confirmationui/fuzz/msg_fuzzer.cpp173
-rw-r--r--trusty/confirmationui/include/TrustyIpc.h80
-rw-r--r--trusty/coverage/Android.bp53
-rw-r--r--trusty/coverage/coverage.cpp306
-rw-r--r--trusty/coverage/coverage_test.cpp102
-rw-r--r--trusty/coverage/include/trusty/coverage/coverage.h82
-rw-r--r--trusty/coverage/include/trusty/coverage/record.h70
-rw-r--r--trusty/coverage/include/trusty/coverage/tipc.h62
-rw-r--r--trusty/coverage/include/trusty/coverage/uuid.h35
-rw-r--r--trusty/coverage/uuid.cpp44
-rw-r--r--trusty/fuzz/Android.bp62
-rw-r--r--trusty/fuzz/counters.cpp96
-rw-r--r--trusty/fuzz/include/trusty/fuzz/counters.h42
-rw-r--r--trusty/fuzz/include/trusty/fuzz/utils.h51
-rw-r--r--trusty/fuzz/test/Android.bp31
-rw-r--r--trusty/fuzz/tipc_fuzzer.cpp96
-rw-r--r--trusty/fuzz/utils.cpp141
-rw-r--r--trusty/gatekeeper/Android.bp4
-rw-r--r--trusty/gatekeeper/fuzz/Android.bp33
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-2MMzSrbin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Et63W0bin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-G41Iz8bin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-ItEoqJbin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-MGXdfubin92 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Yq4f10bin92 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-agxKZabin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-alhn2vbin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-eVJFHVbin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-et5K21bin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-gun5YXbin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-kXw1R9bin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-moapssbin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-u5QySbbin92 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-uZtvkqbin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-w5G2SFbin60 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-y3H74xbin122 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-yALfeSbin154 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-2S1GLibin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-4j7hUcbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-6hsSQGbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-E8CE7bbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-GEDmHjbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-MpwDENbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Qutf8Obin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Sg1WMtbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-U6Y1Mybin78 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-WdSRkybin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Ypw6WPbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Yyj4Afbin78 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-amyF62bin78 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-gu8ziAbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-iCATsMbin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-kawT3Ibin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-sYFzM5bin90 -> 0 bytes
-rw-r--r--trusty/gatekeeper/fuzz/corpus/gatekeeper-send-yNFMdnbin78 -> 0 bytes
-rw-r--r--trusty/gatekeeper/gatekeeper_ipc.h10
-rw-r--r--trusty/gatekeeper/trusty_gatekeeper.cpp47
-rw-r--r--trusty/gatekeeper/trusty_gatekeeper.h9
-rw-r--r--trusty/keymaster/3.0/TrustyKeymaster3Device.cpp56
-rw-r--r--trusty/keymaster/3.0/service.cpp2
-rw-r--r--trusty/keymaster/4.0/TrustyKeymaster4Device.cpp62
-rw-r--r--trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml6
-rw-r--r--trusty/keymaster/4.0/service.cpp2
-rw-r--r--trusty/keymaster/Android.bp161
-rw-r--r--trusty/keymaster/TrustyKeymaster.cpp123
-rw-r--r--trusty/keymaster/fuzz/Android.bp33
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5bin36 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mxbin112 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8bin12 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHibin174 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCRbin36 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FNbin48 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRAbin612 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgHbin174 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXqbin12 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5Tbin12 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dObin174 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60bin48 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5Dbin36 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1tbin36 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQKbin612 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1bin174 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmgbin16 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksubin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1Ubin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4nbin36 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjnebin612 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39bin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrYbin612 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsHbin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIAbin636 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgrbin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9Ibin56 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-7X098Zbin702 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4bin48 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LFbin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxgbin702 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoCbin44 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-PzXetKbin48 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3Dbin702 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJWbin56 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7bin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3bin44 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiXbin48 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rkbin44 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wMbin702 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHvbin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-jiL5ypbin12 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxcbin12 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2ybin32 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-ltPKlsbin28 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVPbin44 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkTbin96 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-rhVedcbin56 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Exbin56 -> 0 bytes
-rw-r--r--trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQbin48 -> 0 bytes
-rw-r--r--trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h88
-rw-r--r--trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h64
-rw-r--r--trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h14
-rw-r--r--trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h53
-rw-r--r--trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h38
-rw-r--r--trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h39
-rw-r--r--trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h21
-rw-r--r--trusty/keymaster/ipc/trusty_keymaster_ipc.cpp118
-rw-r--r--trusty/keymaster/keymint/TrustyKeyMintDevice.cpp324
-rw-r--r--trusty/keymaster/keymint/TrustyKeyMintOperation.cpp179
-rw-r--r--trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp120
-rw-r--r--trusty/keymaster/keymint/TrustySecureClock.cpp42
-rw-r--r--trusty/keymaster/keymint/TrustySharedSecret.cpp54
-rw-r--r--trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc4
-rw-r--r--trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml18
-rw-r--r--trusty/keymaster/keymint/service.cpp61
-rw-r--r--trusty/keymaster/legacy/Makefile199
-rw-r--r--trusty/keymaster/legacy/module.cpp62
-rw-r--r--trusty/keymaster/legacy/trusty_keymaster_device.cpp761
-rw-r--r--trusty/keymaster/legacy/trusty_keymaster_device_test.cpp561
-rw-r--r--trusty/keymaster/legacy/trusty_keymaster_main.cpp408
-rw-r--r--trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml116
-rw-r--r--trusty/keymaster/set_attestation_key/set_attestation_key.cpp362
-rw-r--r--trusty/libtrusty/Android.bp16
-rw-r--r--trusty/libtrusty/include/trusty/ipc.h77
-rw-r--r--trusty/libtrusty/include/trusty/tipc.h4
-rw-r--r--trusty/libtrusty/tipc-test/Android.bp5
-rw-r--r--trusty/libtrusty/tipc-test/tipc_test.c128
-rw-r--r--trusty/libtrusty/tipc_ioctl.h26
-rw-r--r--trusty/libtrusty/trusty.c72
-rw-r--r--trusty/metrics/Android.bp51
-rw-r--r--trusty/metrics/include/trusty/metrics/metrics.h57
-rw-r--r--trusty/metrics/include/trusty/metrics/tipc.h103
-rw-r--r--trusty/metrics/metrics.cpp142
-rw-r--r--trusty/metrics/metrics_test.cpp139
-rw-r--r--trusty/secure_dpu/Android.bp24
-rw-r--r--trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h113
-rw-r--r--trusty/storage/interface/Android.bp4
-rw-r--r--trusty/storage/lib/Android.bp4
-rw-r--r--trusty/storage/proxy/Android.bp9
-rw-r--r--trusty/storage/proxy/rpmb.c279
-rw-r--r--trusty/storage/proxy/storage.c1
-rw-r--r--trusty/storage/tests/Android.bp4
-rw-r--r--trusty/trusty-base.mk5
-rw-r--r--trusty/trusty-test.mk19
-rw-r--r--trusty/utils/acvp/Android.bp40
-rw-r--r--trusty/utils/acvp/acvp_ipc.h77
-rw-r--r--trusty/utils/acvp/trusty_modulewrapper.cpp235
-rw-r--r--trusty/utils/rpmb_dev/Android.bp10
-rw-r--r--trusty/utils/rpmb_dev/rpmb_dev.c3
-rw-r--r--trusty/utils/rpmb_dev/rpmb_dev.rc2
-rw-r--r--trusty/utils/spiproxyd/Android.bp40
-rw-r--r--trusty/utils/spiproxyd/main.c136
-rw-r--r--trusty/utils/spiproxyd/proxy.rc20
-rw-r--r--trusty/utils/trusty-ut-ctrl/Android.bp7
-rw-r--r--usbd/Android.bp4
-rw-r--r--watchdogd/Android.bp4
1485 files changed, 108185 insertions, 39819 deletions
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 000000000..0b4a9255c
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,4 @@
+filegroup {
+ name: "android_filesystem_config_header",
+ srcs: ["include/private/android_filesystem_config.h"],
+}
diff --git a/METADATA b/METADATA
deleted file mode 100644
index d97975ca3..000000000
--- a/METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-third_party {
- license_type: NOTICE
-}
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 000000000..152be2059
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,324 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Android-specific code. ==
+ =========================================================================
+
+Android Code
+Copyright 2005-2008 The Android Open Source Project
+
+This product includes software developed as part of
+The Android Open Source Project (http://source.android.com).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for Apache Commons code. ==
+ =========================================================================
+
+Apache Commons
+Copyright 1999-2006 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for Jakarta Commons Logging. ==
+ =========================================================================
+
+Jakarta Commons Logging (JCL)
+Copyright 2005,2006 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Nuance code. ==
+ =========================================================================
+
+These files are Copyright 2007 Nuance Communications, but released under
+the Apache2 License.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Media Codecs code. ==
+ =========================================================================
+
+Media Codecs
+These files are Copyright 1998 - 2009 PacketVideo, but released under
+the Apache2 License.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the TagSoup code. ==
+ =========================================================================
+
+This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
+
+TagSoup is licensed under the Apache License,
+Version 2.0. You may obtain a copy of this license at
+http://www.apache.org/licenses/LICENSE-2.0 . You may also have
+additional legal rights not granted by this license.
+
+TagSoup is distributed in the hope that it will be useful, but
+unless required by applicable law or agreed to in writing, TagSoup
+is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied; not even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for Additional Codecs code. ==
+ =========================================================================
+
+Additional Codecs
+These files are Copyright 2003-2010 VisualOn, but released under
+the Apache2 License.
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Audio Effects code. ==
+ =========================================================================
+
+Audio Effects
+These files are Copyright (C) 2004-2010 NXP Software and
+Copyright (C) 2010 The Android Open Source Project, but released under
+the Apache2 License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/,
+and http://www.unicode.org/cldr/data/ . Unicode Software includes any
+source code published in the Unicode Standard or under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA
+FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY
+ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF
+THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
+DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished to
+do so, provided that (a) the above copyright notice(s) and this permission
+notice appear with all copies of the Data Files or Software, (b) both the
+above copyright notice(s) and this permission notice appear in associated
+documentation, and (c) there is clear notice in each modified Data File
+or in the Software as well as in the documentation associated with the
+Data File(s) or Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index dcf92be1e..c8dbf77fa 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,6 +3,3 @@ clang_format = true
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
-
-[Hook Scripts]
-aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index da7fca1e7..0ec505da8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,28 +7,22 @@
"name": "adb_crypto_test"
},
{
- "name": "adb_pairing_auth_test"
- },
- {
- "name": "adb_pairing_connection_test"
- },
- {
"name": "adb_tls_connection_test"
},
{
- "name": "CtsFsMgrTestCases"
- },
- {
"name": "CtsInitTestCases"
},
{
"name": "debuggerd_test"
},
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "init_kill_services_test"
+ "name": "libbase_test"
},
{
"name": "libpackagelistparser_test"
@@ -52,15 +46,17 @@
"name": "memunreachable_unit_test"
},
{
+ "name": "memunreachable_unit_test",
+ "host": true
+ },
+ {
"name": "memunreachable_binder_test"
},
{
"name": "propertyinfoserializer_tests"
- }
- ],
- "imports": [
+ },
{
- "path": "frameworks/base/tests/StagedInstallTest"
+ "name": "ziparchive-tests"
}
]
}
diff --git a/libcrypto_utils/.clang-format b/base/.clang-format
index fd0645fdf..fd0645fdf 120000
--- a/libcrypto_utils/.clang-format
+++ b/base/.clang-format
diff --git a/base/Android.bp b/base/Android.bp
new file mode 100644
index 000000000..3d96e4209
--- /dev/null
+++ b/base/Android.bp
@@ -0,0 +1,225 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libbase_cflags_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ target: {
+ android: {
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ },
+ },
+}
+
+cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ ramdisk_available: true,
+ recovery_available: true,
+ host_supported: true,
+ native_bridge_supported: true,
+ export_include_dirs: ["include"],
+
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "29",
+}
+
+cc_defaults {
+ name: "libbase_defaults",
+ defaults: ["libbase_cflags_defaults"],
+ srcs: [
+ "abi_compatibility.cpp",
+ "chrono_utils.cpp",
+ "cmsg.cpp",
+ "file.cpp",
+ "liblog_symbols.cpp",
+ "logging.cpp",
+ "mapped_file.cpp",
+ "parsebool.cpp",
+ "parsenetaddress.cpp",
+ "process.cpp",
+ "properties.cpp",
+ "stringprintf.cpp",
+ "strings.cpp",
+ "threads.cpp",
+ "test_utils.cpp",
+ ],
+
+ cppflags: ["-Wexit-time-destructors"],
+ shared_libs: ["liblog"],
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+
+ },
+ linux: {
+ srcs: [
+ "errors_unix.cpp",
+ ],
+ },
+ darwin: {
+ srcs: [
+ "errors_unix.cpp",
+ ],
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ srcs: [
+ "errors_windows.cpp",
+ "utf8.cpp",
+ ],
+ exclude_srcs: [
+ "cmsg.cpp",
+ ],
+ enabled: true,
+ },
+ },
+ min_sdk_version: "29",
+}
+
+cc_library {
+ name: "libbase",
+ defaults: ["libbase_defaults"],
+ vendor_available: true,
+ ramdisk_available: true,
+ recovery_available: true,
+ host_supported: true,
+ native_bridge_supported: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ header_libs: [
+ "libbase_headers",
+ ],
+ export_header_lib_headers: ["libbase_headers"],
+ static_libs: ["fmtlib"],
+ whole_static_libs: ["fmtlib"],
+ export_static_lib_headers: ["fmtlib"],
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+}
+
+cc_library_static {
+ name: "libbase_ndk",
+ defaults: ["libbase_defaults"],
+ sdk_version: "current",
+ stl: "c++_static",
+ export_include_dirs: ["include"],
+ static_libs: ["fmtlib_ndk"],
+ whole_static_libs: ["fmtlib_ndk"],
+ export_static_lib_headers: ["fmtlib_ndk"],
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+ name: "libbase_test",
+ defaults: ["libbase_cflags_defaults"],
+ host_supported: true,
+ srcs: [
+ "cmsg_test.cpp",
+ "endian_test.cpp",
+ "errors_test.cpp",
+ "expected_test.cpp",
+ "file_test.cpp",
+ "logging_splitters_test.cpp",
+ "logging_test.cpp",
+ "macros_test.cpp",
+ "mapped_file_test.cpp",
+ "no_destructor_test.cpp",
+ "parsedouble_test.cpp",
+ "parsebool_test.cpp",
+ "parseint_test.cpp",
+ "parsenetaddress_test.cpp",
+ "process_test.cpp",
+ "properties_test.cpp",
+ "result_test.cpp",
+ "scopeguard_test.cpp",
+ "stringprintf_test.cpp",
+ "strings_test.cpp",
+ "test_main.cpp",
+ "test_utils_test.cpp",
+ ],
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
+ linux: {
+ srcs: ["chrono_utils_test.cpp"],
+ },
+ windows: {
+ srcs: ["utf8_test.cpp"],
+ cflags: ["-Wno-unused-parameter"],
+ enabled: true,
+ },
+ },
+ local_include_dirs: ["."],
+ shared_libs: ["libbase"],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+ name: "libbase_benchmark",
+ defaults: ["libbase_cflags_defaults"],
+
+ srcs: ["format_benchmark.cpp"],
+ shared_libs: ["libbase"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg
new file mode 100644
index 000000000..d94a89c34
--- /dev/null
+++ b/base/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators
diff --git a/base/OWNERS b/base/OWNERS
new file mode 100644
index 000000000..97777f703
--- /dev/null
+++ b/base/OWNERS
@@ -0,0 +1,3 @@
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/base/README.md b/base/README.md
new file mode 100644
index 000000000..2ef5c10b5
--- /dev/null
+++ b/base/README.md
@@ -0,0 +1,42 @@
+# libbase
+
+## Who is this library for?
+
+This library is a collection of convenience functions to make common tasks
+easier and less error-prone.
+
+In this context, "error-prone" covers both "hard to do correctly" and
+"hard to do with good performance", but as a general purpose library,
+libbase's primary focus is on making it easier to do things easily and
+correctly when a compromise has to be made between "simplest API" on the
+one hand and "fastest implementation" on the other. Though obviously
+the ideal is to have both.
+
+## Should my routine be added?
+
+The intention is to cover the 80% use cases, not be all things to all users.
+
+If you have a routine that's really useful in your project,
+congratulations. But that doesn't mean it should be here rather than
+just in your project.
+
+The question for libbase is "should everyone be doing this?"/"does this
+make everyone's code cleaner/safer?". Historically we've considered the
+bar for inclusion to be "are there at least three *unrelated* projects
+that would be cleaned up by doing so".
+
+If your routine is actually something from a future C++ standard (that
+isn't yet in libc++), or it's widely used in another library, that helps
+show that there's precedent. Being able to say "so-and-so has used this
+API for n years" is a good way to reduce concerns about API choices.
+
+## Any other restrictions?
+
+Unlike most Android code, code in libbase has to build for Mac and
+Windows too.
+
+Code here is also expected to have good test coverage.
+
+By its nature, it's difficult to change libbase API. It's often best
+to start using your routine just in your project, and let it "graduate"
+after you're certain that the API is solid.
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
new file mode 100644
index 000000000..06a780171
--- /dev/null
+++ b/base/abi_compatibility.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include "android-base/cmsg.h"
+#include "android-base/file.h"
+#include "android-base/mapped_file.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace base {
+
+// These ABI-compatibility shims are in a separate file for two reasons:
+// 1. If they were in the file with the actual functions, it prevents calls to
+// those functions by other functions in the file, due to ambiguity.
+// 2. We will hopefully be able to delete these quickly.
+
+#if !defined(_WIN32)
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+ std::vector<unique_fd>* fds) {
+ return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
+}
+#endif
+
+bool ReadFdToString(int fd, std::string* content) {
+ return ReadFdToString(borrowed_fd(fd), content);
+}
+
+bool WriteStringToFd(const std::string& content, int fd) {
+ return WriteStringToFd(content, borrowed_fd(fd));
+}
+
+bool ReadFully(int fd, void* data, size_t byte_count) {
+ return ReadFully(borrowed_fd(fd), data, byte_count);
+}
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+ return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+ return WriteFully(borrowed_fd(fd), data, byte_count);
+}
+
+#if defined(__LP64__)
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
+#else
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
+#endif
+
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
+ int prot) {
+ return MappedFile::FromFd(fd, offset, length, prot);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
new file mode 100644
index 000000000..19080a5a7
--- /dev/null
+++ b/base/chrono_utils.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/chrono_utils.h"
+
+#include <time.h>
+
+namespace android {
+namespace base {
+
+boot_clock::time_point boot_clock::now() {
+#ifdef __linux__
+ timespec ts;
+ clock_gettime(CLOCK_BOOTTIME, &ts);
+ return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+ std::chrono::nanoseconds(ts.tv_nsec));
+#else
+ // Darwin and Windows do not support clock_gettime.
+ return boot_clock::time_point();
+#endif // __linux__
+}
+
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+ os << t.duration().count() << "ms";
+ return os;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
new file mode 100644
index 000000000..da442f455
--- /dev/null
+++ b/base/chrono_utils_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/chrono_utils.h"
+
+#include <time.h>
+
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+std::chrono::seconds GetBootTimeSeconds() {
+ struct timespec now;
+ clock_gettime(CLOCK_BOOTTIME, &now);
+
+ auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
+ std::chrono::nanoseconds(now.tv_nsec));
+ return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
+}
+
+// Tests (at least) the seconds accuracy of the boot_clock::now() method.
+TEST(ChronoUtilsTest, BootClockNowSeconds) {
+ auto now = GetBootTimeSeconds();
+ auto boot_seconds =
+ std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
+ EXPECT_EQ(now, boot_seconds);
+}
+
+template <typename T>
+void ExpectAboutEqual(T expected, T actual) {
+ auto expected_upper_bound = expected * 1.05f;
+ auto expected_lower_bound = expected * .95;
+ EXPECT_GT(expected_upper_bound, actual);
+ EXPECT_LT(expected_lower_bound, actual);
+}
+
+TEST(ChronoUtilsTest, TimerDurationIsSane) {
+ auto start = boot_clock::now();
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop = boot_clock::now();
+ auto stop_timer = t.duration();
+
+ auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
+ ExpectAboutEqual(expected, stop_timer);
+}
+
+TEST(ChronoUtilsTest, TimerOstream) {
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop_timer = t.duration().count();
+ std::stringstream os;
+ os << t;
+ decltype(stop_timer) stop_timer_from_stream;
+ os >> stop_timer_from_stream;
+ EXPECT_NE(0, stop_timer);
+ ExpectAboutEqual(stop_timer, stop_timer_from_stream);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 000000000..1fa873c82
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+ size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+ if (cmsg_space >= PAGE_SIZE) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+ iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+ msghdr msg = {
+ .msg_name = nullptr,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ // We can't cast to the actual type of the field, because it's different across platforms.
+ .msg_controllen = static_cast<unsigned int>(cmsg_space),
+ .msg_flags = 0,
+ };
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = cmsg_len;
+
+ int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ for (size_t i = 0; i < fds.size(); ++i) {
+ cmsg_fds[i] = fds[i];
+ }
+
+#if defined(__linux__)
+ int flags = MSG_NOSIGNAL;
+#else
+ int flags = 0;
+#endif
+
+ return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
+ std::vector<unique_fd>* fds) {
+ fds->clear();
+
+ size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+ if (cmsg_space >= PAGE_SIZE) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+ iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+ msghdr msg = {
+ .msg_name = nullptr,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ // We can't cast to the actual type of the field, because it's different across platforms.
+ .msg_controllen = static_cast<unsigned int>(cmsg_space),
+ .msg_flags = 0,
+ };
+
+ int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+ flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+ ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
+
+ if (rc == -1) {
+ return -1;
+ }
+
+ int error = 0;
+ if ((msg.msg_flags & MSG_TRUNC)) {
+ LOG(ERROR) << "message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ } else if ((msg.msg_flags & MSG_CTRUNC)) {
+ LOG(ERROR) << "control message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ }
+
+ std::vector<unique_fd> received_fds;
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+ << "]";
+ error = EBADMSG;
+ continue;
+ }
+
+ // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+ // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+ CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+ if (cmsg->cmsg_len % sizeof(int) != 0) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+ } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+ }
+
+ int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+ // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+ fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+ received_fds.emplace_back(cmsg_fds[i]);
+ }
+ }
+
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ if (received_fds.size() > max_fds) {
+ LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+ << received_fds.size();
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ *fds = std::move(received_fds);
+ return rc;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 000000000..9ee5c8253
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ PLOG(FATAL) << "fstat failed";
+ }
+
+ return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+ bool Seqpacket() { return GetParam(); }
+
+ void SetUp() override {
+ ASSERT_TRUE(
+ android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+ int dup1 = dup(tmp1.fd);
+ ASSERT_NE(-1, dup1);
+ int dup2 = dup(tmp2.fd);
+ ASSERT_NE(-1, dup2);
+
+ fd1.reset(dup1);
+ fd2.reset(dup2);
+
+ ino1 = GetInode(dup1);
+ ino2 = GetInode(dup2);
+ }
+
+ unique_fd send;
+ unique_fd recv;
+
+ TemporaryFile tmp1;
+ TemporaryFile tmp2;
+
+ unique_fd fd1;
+ unique_fd fd2;
+
+ ino_t ino1;
+ ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+ ASSERT_EQ('x', buf[0]);
+ ASSERT_NE(-1, received.get());
+
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+
+ ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+ if (Seqpacket()) {
+ ASSERT_EQ(-1, rc);
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received1.get());
+ ASSERT_EQ(-1, received2.get());
+ } else {
+ ASSERT_EQ(1, rc);
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+ ASSERT_EQ(1, read(recv.get(), buf, 2));
+ }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+ char buf[2];
+ ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+ ASSERT_EQ('a', buf[0]);
+
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+ if (Seqpacket()) {
+ return;
+ }
+
+ // fds are associated with the first byte of the write.
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+ char buf[2];
+ ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+ ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+ std::vector<unique_fd> received1;
+ ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_TRUE(received1.empty());
+
+ unique_fd received2;
+ rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('d', buf[0]);
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+ unique_fd sent1(dup(tmp1.fd));
+ unique_fd sent2(dup(tmp2.fd));
+
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+ char buf[2];
+ std::vector<unique_fd> received;
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/endian_test.cpp b/base/endian_test.cpp
new file mode 100644
index 000000000..963ab1348
--- /dev/null
+++ b/base/endian_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/endian.h"
+
+#include <gtest/gtest.h>
+
+TEST(endian, constants) {
+ ASSERT_TRUE(__LITTLE_ENDIAN == LITTLE_ENDIAN);
+ ASSERT_TRUE(__BIG_ENDIAN == BIG_ENDIAN);
+ ASSERT_TRUE(__BYTE_ORDER == BYTE_ORDER);
+
+ ASSERT_EQ(__LITTLE_ENDIAN, __BYTE_ORDER);
+}
+
+TEST(endian, smoke) {
+ static constexpr uint16_t le16 = 0x1234;
+ static constexpr uint32_t le32 = 0x12345678;
+ static constexpr uint64_t le64 = 0x123456789abcdef0;
+
+ static constexpr uint16_t be16 = 0x3412;
+ static constexpr uint32_t be32 = 0x78563412;
+ static constexpr uint64_t be64 = 0xf0debc9a78563412;
+
+ ASSERT_EQ(be16, htons(le16));
+ ASSERT_EQ(be32, htonl(le32));
+ ASSERT_EQ(be64, htonq(le64));
+
+ ASSERT_EQ(le16, ntohs(be16));
+ ASSERT_EQ(le32, ntohl(be32));
+ ASSERT_EQ(le64, ntohq(be64));
+
+ ASSERT_EQ(be16, htobe16(le16));
+ ASSERT_EQ(be32, htobe32(le32));
+ ASSERT_EQ(be64, htobe64(le64));
+
+ ASSERT_EQ(le16, betoh16(be16));
+ ASSERT_EQ(le32, betoh32(be32));
+ ASSERT_EQ(le64, betoh64(be64));
+
+ ASSERT_EQ(le16, htole16(le16));
+ ASSERT_EQ(le32, htole32(le32));
+ ASSERT_EQ(le64, htole64(le64));
+
+ ASSERT_EQ(le16, letoh16(le16));
+ ASSERT_EQ(le32, letoh32(le32));
+ ASSERT_EQ(le64, letoh64(le64));
+
+ ASSERT_EQ(le16, be16toh(be16));
+ ASSERT_EQ(le32, be32toh(be32));
+ ASSERT_EQ(le64, be64toh(be64));
+
+ ASSERT_EQ(le16, le16toh(le16));
+ ASSERT_EQ(le32, le32toh(le32));
+ ASSERT_EQ(le64, le64toh(le64));
+}
diff --git a/base/errors_test.cpp b/base/errors_test.cpp
new file mode 100644
index 000000000..8e7cdd1da
--- /dev/null
+++ b/base/errors_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/errors.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+// Error strings aren't consistent enough across systems to test the output,
+// just make sure we can compile correctly and nothing crashes even if we send
+// it possibly bogus error codes.
+TEST(ErrorsTest, TestSystemErrorString) {
+ SystemErrorCodeToString(-1);
+ SystemErrorCodeToString(0);
+ SystemErrorCodeToString(1);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/errors_unix.cpp b/base/errors_unix.cpp
new file mode 100644
index 000000000..48269b675
--- /dev/null
+++ b/base/errors_unix.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/errors.h"
+
+#include <errno.h>
+#include <string.h>
+
+namespace android {
+namespace base {
+
+std::string SystemErrorCodeToString(int error_code) {
+ return strerror(error_code);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/errors_windows.cpp b/base/errors_windows.cpp
new file mode 100644
index 000000000..a5ff51188
--- /dev/null
+++ b/base/errors_windows.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/errors.h"
+
+#include <windows.h>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "android-base/utf8.h"
+
+// A Windows error code is a DWORD. It's simpler to use an int error code for
+// both Unix and Windows if possible, but if this fails we'll need a different
+// function signature for each.
+static_assert(sizeof(int) >= sizeof(DWORD),
+ "Windows system error codes are too large to fit in an int.");
+
+namespace android {
+namespace base {
+
+static constexpr DWORD kErrorMessageBufferSize = 256;
+
+std::string SystemErrorCodeToString(int int_error_code) {
+ WCHAR msgbuf[kErrorMessageBufferSize];
+ DWORD error_code = int_error_code;
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
+ kErrorMessageBufferSize, nullptr);
+ if (len == 0) {
+ return android::base::StringPrintf(
+ "Error %lu while retrieving message for error %lu", GetLastError(),
+ error_code);
+ }
+
+ // Convert UTF-16 to UTF-8.
+ std::string msg;
+ if (!android::base::WideToUTF8(msgbuf, &msg)) {
+ return android::base::StringPrintf(
+ "Error %lu while converting message for error %lu from UTF-16 to UTF-8",
+ GetLastError(), error_code);
+ }
+
+ // Messages returned by the system end with line breaks.
+ msg = android::base::Trim(msg);
+
+ // There are many Windows error messages compared to POSIX, so include the
+ // numeric error code for easier, quicker, accurate identification. Use
+ // decimal instead of hex because there are decimal ranges like 10000-11999
+ // for Winsock.
+ android::base::StringAppendF(&msg, " (%lu)", error_code);
+ return msg;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
new file mode 100644
index 000000000..47e396a22
--- /dev/null
+++ b/base/expected_test.cpp
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/expected.h"
+
+#include <cstdio>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using android::base::expected;
+using android::base::unexpected;
+
+typedef expected<int, int> exp_int;
+typedef expected<double, double> exp_double;
+typedef expected<std::string, std::string> exp_string;
+typedef expected<std::pair<std::string, int>, int> exp_pair;
+typedef expected<void, int> exp_void;
+
+struct T {
+ int a;
+ int b;
+ T() = default;
+ T(int a, int b) noexcept : a(a), b(b) {}
+};
+bool operator==(const T& x, const T& y) {
+ return x.a == y.a && x.b == y.b;
+}
+bool operator!=(const T& x, const T& y) {
+ return x.a != y.a || x.b != y.b;
+}
+
+struct E {
+ std::string message;
+ int cause;
+ E(const std::string& message, int cause) : message(message), cause(cause) {}
+};
+
+typedef expected<T,E> exp_complex;
+
+TEST(Expected, testDefaultConstructible) {
+ exp_int e;
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ(0, e.value());
+
+ exp_complex e2;
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(T(0,0), e2.value());
+
+ exp_void e3;
+ EXPECT_TRUE(e3.has_value());
+}
+
+TEST(Expected, testCopyConstructible) {
+ exp_int e;
+ exp_int e2 = e;
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(0, e.value());
+ EXPECT_EQ(0, e2.value());
+
+ exp_void e3;
+ exp_void e4 = e3;
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testMoveConstructible) {
+ exp_int e;
+ exp_int e2 = std::move(e);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(0, e.value());
+ EXPECT_EQ(0, e2.value());
+
+ exp_string e3(std::string("hello"));
+ exp_string e4 = std::move(e3);
+
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_EQ("", e3.value()); // e3 is moved
+ EXPECT_EQ("hello", e4.value());
+
+ exp_void e5;
+ exp_void e6 = std::move(e5);
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testCopyConstructibleFromConvertibleType) {
+ exp_double e = 3.3f;
+ exp_int e2 = e;
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3.3f, e.value());
+ EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testMoveConstructibleFromConvertibleType) {
+ exp_double e = 3.3f;
+ exp_int e2 = std::move(e);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3.3f, e.value());
+ EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testConstructibleFromValue) {
+ exp_int e = 3;
+ exp_double e2 = 5.5f;
+ exp_string e3 = std::string("hello");
+ exp_complex e4 = T(10, 20);
+ exp_void e5 = {};
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_EQ(3, e.value());
+ EXPECT_EQ(5.5f, e2.value());
+ EXPECT_EQ("hello", e3.value());
+ EXPECT_EQ(T(10,20), e4.value());
+}
+
+TEST(Expected, testConstructibleFromMovedValue) {
+ std::string hello = "hello";
+ exp_string e = std::move(hello);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("hello", e.value());
+ EXPECT_EQ("", hello);
+}
+
+TEST(Expected, testConstructibleFromConvertibleValue) {
+ exp_int e = 3.3f; // double to int
+ exp_string e2 = "hello"; // char* to std::string
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ(3, e.value());
+
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ("hello", e2.value());
+}
+
+TEST(Expected, testConstructibleFromUnexpected) {
+ exp_int::unexpected_type unexp = unexpected(10);
+ exp_int e = unexp;
+
+ exp_double::unexpected_type unexp2 = unexpected(10.5f);
+ exp_double e2 = unexp2;
+
+ exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
+ exp_string e3 = unexp3;
+
+ exp_void::unexpected_type unexp4 = unexpected(10);
+ exp_void e4 = unexp4;
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e.error());
+ EXPECT_EQ(10.5f, e2.error());
+ EXPECT_EQ("error", e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testMoveConstructibleFromUnexpected) {
+ exp_int e = unexpected(10);
+ exp_double e2 = unexpected(10.5f);
+ exp_string e3 = unexpected(std::string("error"));
+ exp_void e4 = unexpected(10);
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e.error());
+ EXPECT_EQ(10.5f, e2.error());
+ EXPECT_EQ("error", e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testConstructibleByForwarding) {
+ exp_string e(std::in_place, 5, 'a');
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("aaaaa", e.value());
+
+ exp_string e2({'a', 'b', 'c'});
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ("abc", e2.value());
+
+ exp_pair e3({"hello", 30});
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_EQ("hello",e3->first);
+ EXPECT_EQ(30,e3->second);
+
+ exp_void e4({});
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testDestructible) {
+ bool destroyed = false;
+ struct T {
+ bool* flag_;
+ T(bool* flag) : flag_(flag) {}
+ ~T() { *flag_ = true; }
+ };
+ {
+ expected<T, int> exp = T(&destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(Expected, testAssignable) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ e = e2;
+
+ EXPECT_EQ(20, e.value());
+ EXPECT_EQ(20, e2.value());
+
+ exp_int e3 = 10;
+ exp_int e4 = 20;
+ e3 = std::move(e4);
+
+ EXPECT_EQ(20, e3.value());
+ EXPECT_EQ(20, e4.value());
+
+ exp_void e5 = unexpected(10);
+ ASSERT_FALSE(e5.has_value());
+ exp_void e6;
+ e5 = e6;
+
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testAssignableFromValue) {
+ exp_int e = 10;
+ e = 20;
+ EXPECT_EQ(20, e.value());
+
+ exp_double e2 = 3.5f;
+ e2 = 10.5f;
+ EXPECT_EQ(10.5f, e2.value());
+
+ exp_string e3 = "hello";
+ e3 = "world";
+ EXPECT_EQ("world", e3.value());
+
+ exp_void e4 = unexpected(10);
+ ASSERT_FALSE(e4.has_value());
+ e4 = {};
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testAssignableFromUnexpected) {
+ exp_int e = 10;
+ e = unexpected(30);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ(30, e.error());
+
+ exp_double e2 = 3.5f;
+ e2 = unexpected(10.5f);
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(10.5f, e2.error());
+
+ exp_string e3 = "hello";
+ e3 = unexpected("world");
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_EQ("world", e3.error());
+
+ exp_void e4 = {};
+ e4 = unexpected(10);
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testAssignableFromMovedValue) {
+ std::string world = "world";
+ exp_string e = "hello";
+ e = std::move(world);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("world", e.value());
+ EXPECT_EQ("", world);
+}
+
+TEST(Expected, testAssignableFromMovedUnexpected) {
+ std::string world = "world";
+ exp_string e = "hello";
+ e = unexpected(std::move(world));
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ("world", e.error());
+ EXPECT_EQ("", world);
+}
+
+TEST(Expected, testEmplace) {
+ struct T {
+ int a;
+ double b;
+ T() {}
+ T(int a, double b) noexcept : a(a), b(b) {}
+ };
+ expected<T, int> exp;
+ T& t = exp.emplace(3, 10.5f);
+
+ EXPECT_TRUE(exp.has_value());
+ EXPECT_EQ(3, t.a);
+ EXPECT_EQ(10.5f, t.b);
+ EXPECT_EQ(3, exp.value().a);
+ EXPECT_EQ(10.5, exp.value().b);
+
+ exp_void e = unexpected(10);
+ ASSERT_FALSE(e.has_value());
+ e.emplace();
+ EXPECT_TRUE(e.has_value());
+}
+
+TEST(Expected, testSwapExpectedExpected) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ e.swap(e2);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(20, e.value());
+ EXPECT_EQ(10, e2.value());
+
+ exp_void e3;
+ exp_void e4;
+ e3.swap(e4);
+
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testSwapUnexpectedUnexpected) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(20);
+ e.swap(e2);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(20, e.error());
+ EXPECT_EQ(10, e2.error());
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(20);
+ e3.swap(e4);
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(20, e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testSwapExpectedUnepected) {
+ exp_int e = 10;
+ exp_int e2 = unexpected(30);
+ e.swap(e2);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(30, e.error());
+ EXPECT_EQ(10, e2.value());
+
+ exp_void e3;
+ exp_void e4 = unexpected(10);
+ e3.swap(e4);
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_EQ(10, e3.error());
+}
+
+TEST(Expected, testDereference) {
+ struct T {
+ int a;
+ double b;
+ T() {}
+ T(int a, double b) : a(a), b(b) {}
+ };
+ expected<T, int> exp = T(3, 10.5f);
+
+ EXPECT_EQ(3, exp->a);
+ EXPECT_EQ(10.5f, exp->b);
+
+ EXPECT_EQ(3, (*exp).a);
+ EXPECT_EQ(10.5f, (*exp).b);
+}
+
+TEST(Expected, testTest) {
+ exp_int e = 10;
+ EXPECT_TRUE(e.ok());
+ EXPECT_TRUE(e.has_value());
+
+ exp_int e2 = unexpected(10);
+ EXPECT_FALSE(e2.ok());
+ EXPECT_FALSE(e2.has_value());
+}
+
+TEST(Expected, testGetValue) {
+ exp_int e = 10;
+ EXPECT_EQ(10, e.value());
+ EXPECT_EQ(10, e.value_or(20));
+
+ exp_int e2 = unexpected(10);
+ EXPECT_EQ(10, e2.error());
+ EXPECT_EQ(20, e2.value_or(20));
+}
+
+TEST(Expected, testSameValues) {
+ exp_int e = 10;
+ exp_int e2 = 10;
+ EXPECT_TRUE(e == e2);
+ EXPECT_TRUE(e2 == e);
+ EXPECT_FALSE(e != e2);
+ EXPECT_FALSE(e2 != e);
+
+ exp_void e3;
+ exp_void e4;
+ EXPECT_TRUE(e3 == e4);
+ EXPECT_TRUE(e4 == e3);
+ EXPECT_FALSE(e3 != e4);
+ EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentValues) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+}
+
+TEST(Expected, testValueWithError) {
+ exp_int e = 10;
+ exp_int e2 = unexpected(10);
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+
+ exp_void e3;
+ exp_void e4 = unexpected(10);
+ EXPECT_FALSE(e3 == e4);
+ EXPECT_FALSE(e4 == e3);
+ EXPECT_TRUE(e3 != e4);
+ EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testSameErrors) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(10);
+ EXPECT_TRUE(e == e2);
+ EXPECT_TRUE(e2 == e);
+ EXPECT_FALSE(e != e2);
+ EXPECT_FALSE(e2 != e);
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(10);
+ EXPECT_TRUE(e3 == e4);
+ EXPECT_TRUE(e4 == e3);
+ EXPECT_FALSE(e3 != e4);
+ EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentErrors) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(20);
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(20);
+ EXPECT_FALSE(e3 == e4);
+ EXPECT_FALSE(e4 == e3);
+ EXPECT_TRUE(e3 != e4);
+ EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testCompareWithSameError) {
+ exp_int e = unexpected(10);
+ exp_int::unexpected_type error = 10;
+ EXPECT_TRUE(e == error);
+ EXPECT_TRUE(error == e);
+ EXPECT_FALSE(e != error);
+ EXPECT_FALSE(error != e);
+
+ exp_void e2 = unexpected(10);
+ exp_void::unexpected_type error2 = 10;
+ EXPECT_TRUE(e2 == error2);
+ EXPECT_TRUE(error2 == e2);
+ EXPECT_FALSE(e2 != error2);
+ EXPECT_FALSE(error2 != e2);
+}
+
+TEST(Expected, testCompareWithDifferentError) {
+ exp_int e = unexpected(10);
+ exp_int::unexpected_type error = 20;
+ EXPECT_FALSE(e == error);
+ EXPECT_FALSE(error == e);
+ EXPECT_TRUE(e != error);
+ EXPECT_TRUE(error != e);
+
+ exp_void e2 = unexpected(10);
+ exp_void::unexpected_type error2 = 20;
+ EXPECT_FALSE(e2 == error2);
+ EXPECT_FALSE(error2 == e2);
+ EXPECT_TRUE(e2 != error2);
+ EXPECT_TRUE(error2 != e2);
+}
+
+TEST(Expected, testCompareDifferentType) {
+ expected<int,int> e = 10;
+ expected<int32_t, int> e2 = 10;
+ EXPECT_TRUE(e == e2);
+ e2 = 20;
+ EXPECT_FALSE(e == e2);
+
+ expected<std::string_view,int> e3 = "hello";
+ expected<std::string,int> e4 = "hello";
+ EXPECT_TRUE(e3 == e4);
+ e4 = "world";
+ EXPECT_FALSE(e3 == e4);
+
+ expected<void,int> e5;
+ expected<int,int> e6 = 10;
+ EXPECT_FALSE(e5 == e6);
+ EXPECT_FALSE(e6 == e5);
+}
+
+TEST(Expected, testDivideExample) {
+ struct QR {
+ int quotient;
+ int remainder;
+ QR(int q, int r) noexcept : quotient(q), remainder(r) {}
+ bool operator==(const QR& rhs) const {
+ return quotient == rhs.quotient && remainder == rhs.remainder;
+ }
+ bool operator!=(const QR& rhs) const {
+ return quotient != rhs.quotient || remainder == rhs.remainder;
+ }
+ };
+
+ auto divide = [](int x, int y) -> expected<QR,E> {
+ if (y == 0) {
+ return unexpected(E("divide by zero", -1));
+ } else {
+ return QR(x / y, x % y);
+ }
+ };
+
+ EXPECT_FALSE(divide(10, 0).ok());
+ EXPECT_EQ("divide by zero", divide(10, 0).error().message);
+ EXPECT_EQ(-1, divide(10, 0).error().cause);
+
+ EXPECT_TRUE(divide(10, 3).ok());
+ EXPECT_EQ(QR(3, 1), *divide(10, 3));
+}
+
+TEST(Expected, testPair) {
+ auto test = [](bool yes) -> exp_pair {
+ if (yes) {
+ return exp_pair({"yes", 42});
+ } else {
+ return unexpected(42);
+ }
+ };
+
+ auto r = test(true);
+ EXPECT_TRUE(r.ok());
+ EXPECT_EQ("yes", r->first);
+}
+
+TEST(Expected, testVoid) {
+ auto test = [](bool ok) -> exp_void {
+ if (ok) {
+ return {};
+ } else {
+ return unexpected(10);
+ }
+ };
+
+ auto r = test(true);
+ EXPECT_TRUE(r.ok());
+ r = test(false);
+ EXPECT_FALSE(r.ok());
+ EXPECT_EQ(10, r.error());
+}
+
+// copied from result_test.cpp
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T,
+ typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+ static void Reset() {
+ constructor_called = 0;
+ copy_constructor_called = 0;
+ move_constructor_called = 0;
+ copy_assignment_called = 0;
+ move_assignment_called = 0;
+ }
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+typedef expected<ConstructorTracker, int> exp_track;
+
+TEST(Expected, testNumberOfCopies) {
+ // default constructor
+ ConstructorTracker::Reset();
+ exp_track e("hello");
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // copy constructor
+ ConstructorTracker::Reset();
+ exp_track e2 = e;
+ EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // move constructor
+ ConstructorTracker::Reset();
+ exp_track e3 = std::move(e);
+ EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // construct from lvalue
+ ConstructorTracker::Reset();
+ ConstructorTracker ct = "hello";
+ exp_track e4(ct);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // construct from rvalue
+ ConstructorTracker::Reset();
+ ConstructorTracker ct2 = "hello";
+ exp_track e5(std::move(ct2));
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // copy assignment
+ ConstructorTracker::Reset();
+ exp_track e6 = "hello";
+ exp_track e7 = "world";
+ e7 = e6;
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // move assignment
+ ConstructorTracker::Reset();
+ exp_track e8 = "hello";
+ exp_track e9 = "world";
+ e9 = std::move(e8);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
+
+ // swap
+ ConstructorTracker::Reset();
+ exp_track e10 = "hello";
+ exp_track e11 = "world";
+ std::swap(e10, e11);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNoCopyOnReturn) {
+ auto test = [](const std::string& in) -> exp_track {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+ };
+
+ ConstructorTracker::Reset();
+ auto result1 = test("");
+ ASSERT_TRUE(result1.ok());
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ ConstructorTracker::Reset();
+ auto result2 = test("test2");
+ ASSERT_TRUE(result2.ok());
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ ConstructorTracker::Reset();
+ auto result3 = test("test3");
+ ASSERT_TRUE(result3.ok());
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNested) {
+ expected<exp_string, std::string> e = "hello";
+
+ EXPECT_TRUE(e.ok());
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e.value().has_value());
+ EXPECT_TRUE(e->ok());
+ EXPECT_EQ("hello", e.value().value());
+
+ expected<exp_string, std::string> e2 = unexpected("world");
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e2.ok());
+ EXPECT_EQ("world", e2.error());
+
+ expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_FALSE(e3.value().has_value());
+ EXPECT_TRUE(e3.ok());
+ EXPECT_FALSE(e3->ok());
+ EXPECT_EQ("world", e3.value().error());
+}
+
+constexpr bool equals(const char* a, const char* b) {
+ return (a == nullptr && b == nullptr) ||
+ (a != nullptr && b != nullptr && *a == *b &&
+ (*a == '\0' || equals(a + 1, b + 1)));
+}
+
+TEST(Expected, testConstexpr) {
+ // Compliation error will occur if these expressions can't be
+ // evaluated at compile time
+ constexpr exp_int e(3);
+ constexpr exp_int::unexpected_type err(3);
+ constexpr int i = 4;
+
+ // default constructor
+ static_assert(exp_int().value() == 0);
+ // copy constructor
+ static_assert(exp_int(e).value() == 3);
+ // move constructor
+ static_assert(exp_int(exp_int(4)).value() == 4);
+ // copy construct from value
+ static_assert(exp_int(i).value() == 4);
+ // copy construct from unexpected
+ static_assert(exp_int(err).error() == 3);
+ // move costruct from unexpected
+ static_assert(exp_int(unexpected(3)).error() == 3);
+ // observers
+ static_assert(*exp_int(3) == 3);
+ static_assert(exp_int(3).has_value() == true);
+ static_assert(exp_int(3).value_or(4) == 3);
+
+ typedef expected<const char*, int> exp_s;
+ constexpr exp_s s("hello");
+ constexpr const char* c = "hello";
+ static_assert(equals(exp_s().value(), nullptr));
+ static_assert(equals(exp_s(s).value(), "hello"));
+ static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
+ static_assert(equals(exp_s("hello").value(), "hello"));
+ static_assert(equals(exp_s(c).value(), "hello"));
+}
+
+TEST(Expected, testWithNonConstructible) {
+ struct AssertNotConstructed {
+ AssertNotConstructed() = delete;
+ };
+
+ expected<int, AssertNotConstructed> v(42);
+ EXPECT_TRUE(v.has_value());
+ EXPECT_EQ(42, v.value());
+
+ expected<AssertNotConstructed, int> e(unexpected(42));
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ(42, e.error());
+}
+
+TEST(Expected, testWithMoveOnlyType) {
+ typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
+ exp_ptr e(std::make_unique<int>(3));
+ exp_ptr e2(unexpected(std::make_unique<int>(4)));
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(3, *(e.value()));
+ EXPECT_EQ(4, *(e2.error()));
+
+ e2 = std::move(e);
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3, *(e2.value()));
+}
diff --git a/base/file.cpp b/base/file.cpp
new file mode 100644
index 000000000..97cc2b27f
--- /dev/null
+++ b/base/file.cpp
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#endif
+#if defined(_WIN32)
+#include <direct.h>
+#include <windows.h>
+#define O_NOFOLLOW 0
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
+#include "android-base/logging.h" // and must be after windows.h for ERROR
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
+
+namespace {
+
+#ifdef _WIN32
+static int mkstemp(char* name_template, size_t size_in_chars) {
+ std::wstring path;
+ CHECK(android::base::UTF8ToWide(name_template, &path))
+ << "path can't be converted to wchar: " << name_template;
+ if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
+ return -1;
+ }
+
+ // Use open() to match the close() that TemporaryFile's destructor does.
+ // Use O_BINARY to match base file APIs.
+ int fd = _wopen(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ return -1;
+ }
+
+ std::string path_utf8;
+ CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
+ CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
+ << "utf8 path can't be assigned back to name_template";
+
+ return fd;
+}
+
+static char* mkdtemp(char* name_template, size_t size_in_chars) {
+ std::wstring path;
+ CHECK(android::base::UTF8ToWide(name_template, &path))
+ << "path can't be converted to wchar: " << name_template;
+
+ if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
+ return nullptr;
+ }
+
+ if (_wmkdir(path.c_str()) != 0) {
+ return nullptr;
+ }
+
+ std::string path_utf8;
+ CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
+ CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
+ << "utf8 path can't be assigned back to name_template";
+
+ return name_template;
+}
+#endif
+
+std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+ const auto* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
+ if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
+ return tmpdir;
+ }
+ // Tests running in app context can't access /data/local/tmp,
+ // so try current directory if /data/local/tmp is not accessible.
+ return ".";
+#elif defined(_WIN32)
+ wchar_t tmp_dir_w[MAX_PATH];
+ DWORD result = GetTempPathW(std::size(tmp_dir_w), tmp_dir_w); // checks TMP env
+ CHECK_NE(result, 0ul) << "GetTempPathW failed, error: " << GetLastError();
+ CHECK_LT(result, std::size(tmp_dir_w)) << "path truncated to: " << result;
+
+ // GetTempPath() returns a path with a trailing slash, but init()
+ // does not expect that, so remove it.
+ if (tmp_dir_w[result - 1] == L'\\') {
+ tmp_dir_w[result - 1] = L'\0';
+ }
+
+ std::string tmp_dir;
+ CHECK(android::base::WideToUTF8(tmp_dir_w, &tmp_dir)) << "path can't be converted to utf8";
+
+ return tmp_dir;
+#else
+ const auto* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = "/tmp";
+ return tmpdir;
+#endif
+}
+
+} // namespace
+
+TemporaryFile::TemporaryFile() {
+ init(GetSystemTempDir());
+}
+
+TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
+ init(tmp_dir);
+}
+
+TemporaryFile::~TemporaryFile() {
+ if (fd != -1) {
+ close(fd);
+ }
+ if (remove_file_) {
+ unlink(path);
+ }
+}
+
+int TemporaryFile::release() {
+ int result = fd;
+ fd = -1;
+ return result;
+}
+
+void TemporaryFile::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+#if defined(_WIN32)
+ fd = mkstemp(path, sizeof(path));
+#else
+ fd = mkstemp(path);
+#endif
+}
+
+TemporaryDir::TemporaryDir() {
+ init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+ if (!remove_dir_and_contents_) return;
+
+ auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+ switch (file_type) {
+ case FTW_D:
+ case FTW_DP:
+ case FTW_DNR:
+ if (rmdir(child) == -1) {
+ PLOG(ERROR) << "rmdir " << child;
+ }
+ break;
+ case FTW_NS:
+ default:
+ if (rmdir(child) != -1) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+ FALLTHROUGH_INTENDED;
+ case FTW_F:
+ case FTW_SL:
+ case FTW_SLN:
+ if (unlink(child) == -1) {
+ PLOG(ERROR) << "unlink " << child;
+ }
+ break;
+ }
+ return 0;
+ };
+
+ nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+bool TemporaryDir::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+#if defined(_WIN32)
+ return (mkdtemp(path, sizeof(path)) != nullptr);
+#else
+ return (mkdtemp(path) != nullptr);
+#endif
+}
+
+namespace android {
+namespace base {
+
+// Versions of standard library APIs that support UTF-8 strings.
+using namespace android::base::utf8;
+
+bool ReadFdToString(borrowed_fd fd, std::string* content) {
+ content->clear();
+
+ // Although original we had small files in mind, this code gets used for
+ // very large files too, where the std::string growth heuristics might not
+ // be suitable. https://code.google.com/p/android/issues/detail?id=258500.
+ struct stat sb;
+ if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
+ content->reserve(sb.st_size);
+ }
+
+ char buf[BUFSIZ] __attribute__((__uninitialized__));
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
+ content->append(buf, n);
+ }
+ return (n == 0) ? true : false;
+}
+
+bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) {
+ content->clear();
+
+ int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
+ if (fd == -1) {
+ return false;
+ }
+ return ReadFdToString(fd, content);
+}
+
+bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
+ const char* p = content.data();
+ size_t left = content.size();
+ while (left > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
+ if (n == -1) {
+ return false;
+ }
+ p += n;
+ left -= n;
+ }
+ return true;
+}
+
+static bool CleanUpAfterFailedWrite(const std::string& path) {
+ // Something went wrong. Let's not leave a corrupt file lying around.
+ int saved_errno = errno;
+ unlink(path.c_str());
+ errno = saved_errno;
+ return false;
+}
+
+#if !defined(_WIN32)
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ mode_t mode, uid_t owner, gid_t group,
+ bool follow_symlinks) {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
+ (follow_symlinks ? 0 : O_NOFOLLOW);
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
+ if (fd == -1) {
+ PLOG(ERROR) << "android::WriteStringToFile open failed";
+ return false;
+ }
+
+ // We do an explicit fchmod here because we assume that the caller really
+ // meant what they said and doesn't want the umask-influenced mode.
+ if (fchmod(fd, mode) == -1) {
+ PLOG(ERROR) << "android::WriteStringToFile fchmod failed";
+ return CleanUpAfterFailedWrite(path);
+ }
+ if (fchown(fd, owner, group) == -1) {
+ PLOG(ERROR) << "android::WriteStringToFile fchown failed";
+ return CleanUpAfterFailedWrite(path);
+ }
+ if (!WriteStringToFd(content, fd)) {
+ PLOG(ERROR) << "android::WriteStringToFile write failed";
+ return CleanUpAfterFailedWrite(path);
+ }
+ return true;
+}
+#endif
+
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ bool follow_symlinks) {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
+ (follow_symlinks ? 0 : O_NOFOLLOW);
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666)));
+ if (fd == -1) {
+ return false;
+ }
+ return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
+}
+
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
+ if (n <= 0) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+#if defined(_WIN32)
+// Windows implementation of pread. Note that this DOES move the file descriptors read position,
+// but it does so atomically.
+static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
+ DWORD bytes_read;
+ OVERLAPPED overlapped;
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = static_cast<DWORD>(offset);
+ overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+ if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
+ static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
+ // In case someone tries to read errno (since this is masquerading as a POSIX call)
+ errno = EIO;
+ return -1;
+ }
+ return static_cast<ssize_t>(bytes_read);
+}
+#endif
+
+bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ while (byte_count > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
+ if (n <= 0) return false;
+ p += n;
+ byte_count -= n;
+ offset += n;
+ }
+ return true;
+}
+
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
+ if (n == -1) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+bool RemoveFileIfExists(const std::string& path, std::string* err) {
+ struct stat st;
+#if defined(_WIN32)
+ // TODO: Windows version can't handle symbolic links correctly.
+ int result = stat(path.c_str(), &st);
+ bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
+#else
+ int result = lstat(path.c_str(), &st);
+ bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
+#endif
+ if (result == -1) {
+ if (errno == ENOENT || errno == ENOTDIR) return true;
+ if (err != nullptr) *err = strerror(errno);
+ return false;
+ }
+
+ if (result == 0) {
+ if (!file_type_removable) {
+ if (err != nullptr) {
+ *err = "is not a regular file or symbolic link";
+ }
+ return false;
+ }
+ if (unlink(path.c_str()) == -1) {
+ if (err != nullptr) {
+ *err = strerror(errno);
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+#if !defined(_WIN32)
+bool Readlink(const std::string& path, std::string* result) {
+ result->clear();
+
+ // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
+ // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
+ // waste memory to just start there. We add 1 so that we can recognize
+ // whether it actually fit (rather than being truncated to 4095).
+ std::vector<char> buf(4095 + 1);
+ while (true) {
+ ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
+ // Unrecoverable error?
+ if (size == -1) return false;
+ // It fit! (If size == buf.size(), it may have been truncated.)
+ if (static_cast<size_t>(size) < buf.size()) {
+ result->assign(&buf[0], size);
+ return true;
+ }
+ // Double our buffer and try again.
+ buf.resize(buf.size() * 2);
+ }
+}
+#endif
+
+#if !defined(_WIN32)
+bool Realpath(const std::string& path, std::string* result) {
+ result->clear();
+
+ // realpath may exit with EINTR. Retry if so.
+ char* realpath_buf = nullptr;
+ do {
+ realpath_buf = realpath(path.c_str(), nullptr);
+ } while (realpath_buf == nullptr && errno == EINTR);
+
+ if (realpath_buf == nullptr) {
+ return false;
+ }
+ result->assign(realpath_buf);
+ free(realpath_buf);
+ return true;
+}
+#endif
+
+std::string GetExecutablePath() {
+#if defined(__linux__)
+ std::string path;
+ android::base::Readlink("/proc/self/exe", &path);
+ return path;
+#elif defined(__APPLE__)
+ char path[PATH_MAX + 1];
+ uint32_t path_len = sizeof(path);
+ int rc = _NSGetExecutablePath(path, &path_len);
+ if (rc < 0) {
+ std::unique_ptr<char> path_buf(new char[path_len]);
+ _NSGetExecutablePath(path_buf.get(), &path_len);
+ return path_buf.get();
+ }
+ return path;
+#elif defined(_WIN32)
+ char path[PATH_MAX + 1];
+ DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
+ if (result == 0 || result == sizeof(path) - 1) return "";
+ path[PATH_MAX - 1] = 0;
+ return path;
+#else
+#error unknown OS
+#endif
+}
+
+std::string GetExecutableDirectory() {
+ return Dirname(GetExecutablePath());
+}
+
+std::string Basename(const std::string& path) {
+ // Copy path because basename may modify the string passed in.
+ std::string result(path);
+
+#if !defined(__BIONIC__)
+ // Use lock because basename() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to basename in the process also grab this same lock, but its
+ // better than nothing. Bionic's basename returns a thread-local buffer.
+ static std::mutex& basename_lock = *new std::mutex();
+ std::lock_guard<std::mutex> lock(basename_lock);
+#endif
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* name = basename(&result[0]);
+
+ // In case basename returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(name);
+
+ return result;
+}
+
+std::string Dirname(const std::string& path) {
+ // Copy path because dirname may modify the string passed in.
+ std::string result(path);
+
+#if !defined(__BIONIC__)
+ // Use lock because dirname() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to dirname in the process also grab this same lock, but its
+ // better than nothing. Bionic's dirname returns a thread-local buffer.
+ static std::mutex& dirname_lock = *new std::mutex();
+ std::lock_guard<std::mutex> lock(dirname_lock);
+#endif
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* parent = dirname(&result[0]);
+
+ // In case dirname returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(parent);
+
+ return result;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
new file mode 100644
index 000000000..120228d94
--- /dev/null
+++ b/base/file_test.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/file.h"
+
+#include "android-base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#include <string>
+
+#if !defined(_WIN32)
+#include <pwd.h>
+#else
+#include <windows.h>
+#endif
+
+#include "android-base/logging.h" // and must be after windows.h for ERROR
+
+TEST(file, ReadFileToString_ENOENT) {
+ std::string s("hello");
+ errno = 0;
+ ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s));
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(file, ReadFileToString_WriteStringToFile) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
+ << strerror(errno);
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
+ << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+
+// symlinks require elevated privileges on Windows.
+#if !defined(_WIN32)
+TEST(file, ReadFileToString_WriteStringToFile_symlink) {
+ TemporaryFile target, link;
+ ASSERT_EQ(0, unlink(link.path));
+ ASSERT_EQ(0, symlink(target.path, link.path));
+ ASSERT_FALSE(android::base::WriteStringToFile("foo", link.path, false));
+ ASSERT_EQ(ELOOP, errno);
+ ASSERT_TRUE(android::base::WriteStringToFile("foo", link.path, true));
+
+ std::string s;
+ ASSERT_FALSE(android::base::ReadFileToString(link.path, &s));
+ ASSERT_EQ(ELOOP, errno);
+ ASSERT_TRUE(android::base::ReadFileToString(link.path, &s, true));
+ ASSERT_EQ("foo", s);
+}
+#endif
+
+// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
+// sense on Windows.
+#if !defined(_WIN32)
+TEST(file, WriteStringToFile2) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
+ getuid(), getgid()))
+ << strerror(errno);
+ struct stat sb;
+ ASSERT_EQ(0, stat(tf.path, &sb));
+ ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
+ ASSERT_EQ(getuid(), sb.st_uid);
+ ASSERT_EQ(getgid(), sb.st_gid);
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
+ << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+#endif
+
+#if defined(_WIN32)
+TEST(file, NonUnicodeCharsWindows) {
+ constexpr auto kMaxEnvVariableValueSize = 32767;
+ std::wstring old_tmp;
+ old_tmp.resize(kMaxEnvVariableValueSize);
+ old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
+ if (old_tmp.empty()) {
+ // Can't continue with empty TMP folder.
+ return;
+ }
+
+ std::wstring new_tmp = old_tmp;
+ if (new_tmp.back() != L'\\') {
+ new_tmp.push_back(L'\\');
+ }
+
+ {
+ auto path(new_tmp + L"锦绣成都\\");
+ _wmkdir(path.c_str());
+ ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+ }
+ {
+ auto path(new_tmp + L"директория с длинным именем\\");
+ _wmkdir(path.c_str());
+ ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+ }
+ {
+ auto path(new_tmp + L"äüöß weiß\\");
+ _wmkdir(path.c_str());
+ ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+ }
+
+ SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
+}
+
+TEST(file, RootDirectoryWindows) {
+ constexpr auto kMaxEnvVariableValueSize = 32767;
+ std::wstring old_tmp;
+ bool tmp_is_empty = false;
+ old_tmp.resize(kMaxEnvVariableValueSize);
+ old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
+ if (old_tmp.empty()) {
+ tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
+ }
+ SetEnvironmentVariableW(L"TMP", L"C:");
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+
+ SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
+}
+#endif
+
+TEST(file, WriteStringToFd) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+}
+
+TEST(file, WriteFully) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ s.resize(3);
+ ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
+ << strerror(errno);
+ EXPECT_EQ("abc", s);
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ s.resize(1024);
+ ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
+}
+
+TEST(file, RemoveFileIfExists) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ close(tf.fd);
+ tf.fd = -1;
+ std::string err;
+ ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err)) << err;
+ ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path));
+ TemporaryDir td;
+ ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
+ ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
+ ASSERT_EQ("is not a regular file or symbolic link", err);
+}
+
+TEST(file, RemoveFileIfExists_ENOTDIR) {
+ TemporaryFile tf;
+ close(tf.fd);
+ tf.fd = -1;
+ std::string err{"xxx"};
+ ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
+ ASSERT_EQ("xxx", err);
+}
+
+#if !defined(_WIN32)
+TEST(file, RemoveFileIfExists_EACCES) {
+ // EACCES -- one of the directories in the path has no search permission
+ // root can bypass permission restrictions, so drop root.
+ if (getuid() == 0) {
+ passwd* shell = getpwnam("shell");
+ setgid(shell->pw_gid);
+ setuid(shell->pw_uid);
+ }
+
+ TemporaryDir td;
+ TemporaryFile tf(td.path);
+ close(tf.fd);
+ tf.fd = -1;
+ std::string err{"xxx"};
+ // Remove dir's search permission.
+ ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
+ ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
+ ASSERT_EQ("Permission denied", err);
+ // Set dir's search permission again.
+ ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
+ ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
+}
+#endif
+
+TEST(file, Readlink) {
+#if !defined(_WIN32)
+ // Linux doesn't allow empty symbolic links.
+ std::string min("x");
+ // ext2 and ext4 both have PAGE_SIZE limits.
+ // If file encryption is enabled, there's extra overhead to store the
+ // size of the encrypted symlink target. There's also an off-by-one
+ // in current kernels (and marlin/sailfish where we're seeing this
+ // failure are still on 3.18, far from current). http://b/33306057.
+ std::string max(static_cast<size_t>(4096 - 2 - 1 - 1), 'x');
+
+ TemporaryDir td;
+ std::string min_path{std::string(td.path) + "/" + "min"};
+ std::string max_path{std::string(td.path) + "/" + "max"};
+
+ ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
+ ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
+
+ std::string result;
+
+ result = "wrong";
+ ASSERT_TRUE(android::base::Readlink(min_path, &result));
+ ASSERT_EQ(min, result);
+
+ result = "wrong";
+ ASSERT_TRUE(android::base::Readlink(max_path, &result));
+ ASSERT_EQ(max, result);
+#endif
+}
+
+TEST(file, Realpath) {
+#if !defined(_WIN32)
+ TemporaryDir td;
+ std::string basename = android::base::Basename(td.path);
+ std::string dir_name = android::base::Dirname(td.path);
+ std::string base_dir_name = android::base::Basename(dir_name);
+
+ {
+ std::string path = dir_name + "/../" + base_dir_name + "/" + basename;
+ std::string result;
+ ASSERT_TRUE(android::base::Realpath(path, &result));
+ ASSERT_EQ(td.path, result);
+ }
+
+ {
+ std::string path = std::string(td.path) + "/..";
+ std::string result;
+ ASSERT_TRUE(android::base::Realpath(path, &result));
+ ASSERT_EQ(dir_name, result);
+ }
+
+ {
+ errno = 0;
+ std::string path = std::string(td.path) + "/foo.noent";
+ std::string result = "wrong";
+ ASSERT_TRUE(!android::base::Realpath(path, &result));
+ ASSERT_TRUE(result.empty());
+ ASSERT_EQ(ENOENT, errno);
+ }
+#endif
+}
+
+TEST(file, GetExecutableDirectory) {
+ std::string path = android::base::GetExecutableDirectory();
+ ASSERT_NE("", path);
+ ASSERT_NE(android::base::GetExecutablePath(), path);
+ ASSERT_EQ('/', path[0]);
+ ASSERT_NE('/', path[path.size() - 1]);
+}
+
+TEST(file, GetExecutablePath) {
+ ASSERT_NE("", android::base::GetExecutablePath());
+}
+
+TEST(file, Basename) {
+ EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
+ EXPECT_EQ("sh", android::base::Basename("sh"));
+ EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
+}
+
+TEST(file, Dirname) {
+ EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
+ EXPECT_EQ(".", android::base::Dirname("sh"));
+ EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
+}
+
+TEST(file, ReadFileToString_capacity) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+
+ // For a huge file, the overhead should still be small.
+ std::string s;
+ size_t size = 16 * 1024 * 1024;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(size, s.size());
+ EXPECT_LT(s.capacity(), size + 16);
+
+ // Even for weird badly-aligned sizes.
+ size += 12345;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(size, s.size());
+ EXPECT_LT(s.capacity(), size + 16);
+
+ // We'll shrink an enormous string if you read a small file into it.
+ size = 64;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(size, s.size());
+ EXPECT_LT(s.capacity(), size + 16);
+}
+
+TEST(file, ReadFileToString_capacity_0) {
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+
+ // Because /proc reports its files as zero-length, we don't actually trust
+ // any file that claims to be zero-length. Rather than add increasingly
+ // complex heuristics for shrinking the passed-in string in that case, we
+ // currently leave it alone.
+ std::string s;
+ size_t initial_capacity = s.capacity();
+ ASSERT_TRUE(android::base::WriteStringToFile("", tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(0U, s.size());
+ EXPECT_EQ(initial_capacity, s.capacity());
+}
diff --git a/base/format_benchmark.cpp b/base/format_benchmark.cpp
new file mode 100644
index 000000000..9590b2365
--- /dev/null
+++ b/base/format_benchmark.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/format.h"
+
+#include <limits>
+
+#include <benchmark/benchmark.h>
+
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
+
+static void BenchmarkFormatInt(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkFormatInt);
+
+static void BenchmarkStringPrintfInt(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfInt);
+
+static void BenchmarkFormatFloat(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkFormatFloat);
+
+static void BenchmarkStringPrintfFloat(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfFloat);
+
+static void BenchmarkFormatStrings(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
+ }
+}
+
+BENCHMARK(BenchmarkFormatStrings);
+
+static void BenchmarkStringPrintfStrings(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfStrings);
+
+// Run the benchmark
+BENCHMARK_MAIN();
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
new file mode 100644
index 000000000..11fcf710e
--- /dev/null
+++ b/base/include/android-base/chrono_utils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <sstream>
+
+#if __cplusplus > 201103L && !defined(__WIN32) // C++14
+using namespace std::chrono_literals;
+#endif
+
+namespace android {
+namespace base {
+
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+ typedef std::chrono::nanoseconds duration;
+ typedef std::chrono::time_point<boot_clock, duration> time_point;
+
+ static time_point now();
+};
+
+class Timer {
+ public:
+ Timer() : start_(boot_clock::now()) {}
+
+ std::chrono::milliseconds duration() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
+ }
+
+ private:
+ boot_clock::time_point start_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 000000000..e4197b109
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+// - not aligning the cmsg buffer
+// - leaking fds if more fds are received than expected
+// - blindly dereferencing CMSG_DATA without checking the header
+// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+// - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(borrowed_fd sock, const void* data, size_t len,
+ const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sock, void* data, size_t len, size_t max_fds,
+ std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(borrowed_fd sock, const void* data, size_t len, Args&&... sent_fds) {
+ // Do not allow implicit conversion to int: people might try to do something along the lines of:
+ // SendFileDescriptors(..., std::move(a_unique_fd))
+ // and be surprised when the unique_fd isn't closed afterwards.
+ AssertType<int>(std::forward<Args>(sent_fds)...);
+ std::vector<int> fds;
+ Append(fds, std::forward<Args>(sent_fds)...);
+ return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
+ std::vector<unique_fd*> fds;
+ Append(fds, std::forward<Args>(received_fds)...);
+
+ std::vector<unique_fd> result;
+ ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+ if (rc == -1 || result.size() != fds.size()) {
+ int err = rc == -1 ? errno : ENOMSG;
+ for (unique_fd* fd : fds) {
+ fd->reset();
+ }
+ errno = err;
+ return -1;
+ }
+
+ for (size_t i = 0; i < fds.size(); ++i) {
+ *fds[i] = std::move(result[i]);
+ }
+ return rc;
+}
+
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 000000000..be0683ab9
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+// template <typename... Args>
+// std::vector<int> CreateVector(Args&&... args) {
+// std::vector<int> result;
+// Append(result, std::forward<Args>(args)...);
+// return result;
+// }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+ collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+ collection.push_back(std::forward<T>(arg));
+ return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+ AssertType<T>(std::forward<Args>(args)...);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
new file mode 100644
index 000000000..8fa6365ee
--- /dev/null
+++ b/base/include/android-base/endian.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/* A cross-platform equivalent of bionic's <sys/endian.h>. */
+
+/* For __BIONIC__ and __GLIBC__ */
+#include <sys/cdefs.h>
+
+#if defined(__BIONIC__)
+
+#include <sys/endian.h>
+
+#elif defined(__GLIBC__)
+
+/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
+#include <endian.h>
+
+/* glibc keeps htons and htonl in <netinet/in.h>. */
+#include <netinet/in.h>
+
+/* glibc doesn't have the 64-bit variants. */
+#define htonq(x) htobe64(x)
+#define ntohq(x) be64toh(x)
+
+/* glibc has different names to BSD for these. */
+#define betoh16(x) be16toh(x)
+#define betoh32(x) be32toh(x)
+#define betoh64(x) be64toh(x)
+#define letoh16(x) le16toh(x)
+#define letoh32(x) le32toh(x)
+#define letoh64(x) le64toh(x)
+
+#else
+
+#if defined(__APPLE__)
+/* macOS has some of the basics. */
+#include <sys/_endian.h>
+#else
+/* Windows has some of the basics as well. */
+#include <sys/param.h>
+#include <winsock2.h>
+/* winsock2.h *must* be included before the following four macros. */
+#define htons(x) __builtin_bswap16(x)
+#define htonl(x) __builtin_bswap32(x)
+#define ntohs(x) __builtin_bswap16(x)
+#define ntohl(x) __builtin_bswap32(x)
+#endif
+
+/* Neither macOS nor Windows have the rest. */
+
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#define htonq(x) __builtin_bswap64(x)
+
+#define ntohq(x) __builtin_bswap64(x)
+
+#define htobe16(x) __builtin_bswap16(x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htobe64(x) __builtin_bswap64(x)
+
+#define betoh16(x) __builtin_bswap16(x)
+#define betoh32(x) __builtin_bswap32(x)
+#define betoh64(x) __builtin_bswap64(x)
+
+#define htole16(x) (x)
+#define htole32(x) (x)
+#define htole64(x) (x)
+
+#define letoh16(x) (x)
+#define letoh32(x) (x)
+#define letoh64(x) (x)
+
+#define be16toh(x) __builtin_bswap16(x)
+#define be32toh(x) __builtin_bswap32(x)
+#define be64toh(x) __builtin_bswap64(x)
+
+#define le16toh(x) (x)
+#define le32toh(x) (x)
+#define le64toh(x) (x)
+
+#endif
diff --git a/base/include/android-base/errno_restorer.h b/base/include/android-base/errno_restorer.h
new file mode 100644
index 000000000..1c8597c96
--- /dev/null
+++ b/base/include/android-base/errno_restorer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "errno.h"
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+class ErrnoRestorer {
+ public:
+ ErrnoRestorer() : saved_errno_(errno) {}
+
+ ~ErrnoRestorer() { errno = saved_errno_; }
+
+ // Allow this object to be used as part of && operation.
+ operator bool() const { return true; }
+
+ private:
+ const int saved_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
new file mode 100644
index 000000000..06f29fcac
--- /dev/null
+++ b/base/include/android-base/errors.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Portable error handling functions. This is only necessary for host-side
+// code that needs to be cross-platform; code that is only run on Unix should
+// just use errno and strerror() for simplicity.
+//
+// There is some complexity since Windows has (at least) three different error
+// numbers, not all of which share the same type:
+// * errno: for C runtime errors.
+// * GetLastError(): Windows non-socket errors.
+// * WSAGetLastError(): Windows socket errors.
+// errno can be passed to strerror() on all platforms, but the other two require
+// special handling to get the error string. Refer to Microsoft documentation
+// to determine which error code to check for each function.
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace base {
+
+// Returns a string describing the given system error code. |error_code| must
+// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing
+// errno on Windows has undefined behavior.
+std::string SystemErrorCodeToString(int error_code);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
new file mode 100644
index 000000000..9470344e8
--- /dev/null
+++ b/base/include/android-base/expected.h
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+// android::base::expected is an Android implementation of the std::expected
+// proposal.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
+//
+// Usage:
+// using android::base::expected;
+// using android::base::unexpected;
+//
+// expected<double,std::string> safe_divide(double i, double j) {
+// if (j == 0) return unexpected("divide by zero");
+// else return i / j;
+// }
+//
+// void test() {
+// auto q = safe_divide(10, 0);
+// if (q) { printf("%f\n", q.value()); }
+// else { printf("%s\n", q.error().c_str()); }
+// }
+//
+// When the proposal becomes part of the standard and is implemented by
+// libcxx, this will be removed and android::base::expected will be
+// type alias to std::expected.
+//
+
+namespace android {
+namespace base {
+
+// Synopsis
+template<class T, class E>
+ class expected;
+
+template<class E>
+ class unexpected;
+template<class E>
+ unexpected(E) -> unexpected<E>;
+
+template<class E>
+ class bad_expected_access;
+
+template<>
+ class bad_expected_access<void>;
+
+struct unexpect_t {
+ explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+// macros for SFINAE
+#define _ENABLE_IF(...) \
+ , std::enable_if_t<(__VA_ARGS__)>* = nullptr
+
+// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
+// ignored when used as a return value. This is off by default.
+#ifdef NODISCARD_EXPECTED
+#define _NODISCARD_ [[nodiscard]]
+#else
+#define _NODISCARD_
+#endif
+
+// Class expected
+template<class T, class E>
+class _NODISCARD_ expected {
+ public:
+ using value_type = T;
+ using error_type = E;
+ using unexpected_type = unexpected<E>;
+
+ template<class U>
+ using rebind = expected<U, error_type>;
+
+ // constructors
+ constexpr expected() = default;
+ constexpr expected(const expected& rhs) = default;
+ constexpr expected(expected&& rhs) noexcept = default;
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ !(!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(const expected<U, G>& rhs) {
+ if (rhs.has_value()) var_ = rhs.value();
+ else var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ (!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* explicit */
+ )>
+ constexpr explicit expected(const expected<U, G>& rhs) {
+ if (rhs.has_value()) var_ = rhs.value();
+ else var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ !(!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(expected<U, G>&& rhs) {
+ if (rhs.has_value()) var_ = std::move(rhs.value());
+ else var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ (!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* explicit */
+ )>
+ constexpr explicit expected(expected<U, G>&& rhs) {
+ if (rhs.has_value()) var_ = std::move(rhs.value());
+ else var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template <class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ std::is_convertible_v<U&&, T> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
+ constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
+
+ template <class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_convertible_v<U&&, T> /* explicit */
+ )>
+ // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
+ constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, e.value()) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, E(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ std::is_convertible_v<G&&, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ !std::is_convertible_v<G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<T, Args&&...>
+ )>
+ constexpr explicit expected(std::in_place_t, Args&&... args)
+ : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<T, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+ : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<E, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, Args&&... args)
+ : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+ : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+ // destructor
+ ~expected() = default;
+
+ // assignment
+ // Note: SFNAIE doesn't work here because assignment operator should be
+ // non-template. We could workaround this by defining a templated parent class
+ // having the assignment operator. This incomplete implementation however
+ // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+ // assignable. The copy assignment will fail by the underlying std::variant
+ // anyway though the error message won't be clear.
+ expected& operator=(const expected& rhs) = default;
+
+ // Note for SFNAIE above applies to here as well
+ expected& operator=(expected&& rhs) noexcept(
+ std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
+
+ template <class U = T _ENABLE_IF(
+ !std::is_void_v<T> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
+ std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
+ std::is_nothrow_move_constructible_v<E>)>
+ expected& operator=(U&& rhs) {
+ var_ = T(std::forward<U>(rhs));
+ return *this;
+ }
+
+ template<class G = E>
+ expected& operator=(const unexpected<G>& rhs) {
+ var_ = rhs;
+ return *this;
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_nothrow_move_constructible_v<G> &&
+ std::is_move_assignable_v<G>
+ )>
+ expected& operator=(unexpected<G>&& rhs) {
+ var_ = std::move(rhs);
+ return *this;
+ }
+
+ // modifiers
+ template<class... Args _ENABLE_IF(
+ std::is_nothrow_constructible_v<T, Args...>
+ )>
+ T& emplace(Args&&... args) {
+ expected(std::in_place, std::forward<Args>(args)...).swap(*this);
+ return value();
+ }
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
+ )>
+ T& emplace(std::initializer_list<U> il, Args&&... args) {
+ expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
+ return value();
+ }
+
+ // swap
+ template<typename U = T, typename = std::enable_if_t<(
+ std::is_swappable_v<U> &&
+ std::is_swappable_v<E> &&
+ (std::is_move_constructible_v<U> ||
+ std::is_move_constructible_v<E>))>>
+ void swap(expected& rhs) noexcept(
+ std::is_nothrow_move_constructible_v<T> &&
+ std::is_nothrow_swappable_v<T> &&
+ std::is_nothrow_move_constructible_v<E> &&
+ std::is_nothrow_swappable_v<E>) {
+ var_.swap(rhs.var_);
+ }
+
+ // observers
+ constexpr const T* operator->() const { return std::addressof(value()); }
+ constexpr T* operator->() { return std::addressof(value()); }
+ constexpr const T& operator*() const& { return value(); }
+ constexpr T& operator*() & { return value(); }
+ constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
+ constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
+
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ constexpr bool has_value() const noexcept { return var_.index() == 0; }
+ constexpr bool ok() const noexcept { return has_value(); }
+
+ constexpr const T& value() const& { return std::get<T>(var_); }
+ constexpr T& value() & { return std::get<T>(var_); }
+ constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
+ constexpr T&& value() && { return std::move(std::get<T>(var_)); }
+
+ constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+ constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+ constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+ constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+ template<class U _ENABLE_IF(
+ std::is_copy_constructible_v<T> &&
+ std::is_convertible_v<U, T>
+ )>
+ constexpr T value_or(U&& v) const& {
+ if (has_value()) return value();
+ else return static_cast<T>(std::forward<U>(v));
+ }
+
+ template<class U _ENABLE_IF(
+ std::is_move_constructible_v<T> &&
+ std::is_convertible_v<U, T>
+ )>
+ constexpr T value_or(U&& v) && {
+ if (has_value()) return std::move(value());
+ else return static_cast<T>(std::forward<U>(v));
+ }
+
+ // expected equality operators
+ template<class T1, class E1, class T2, class E2>
+ friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
+ template<class T1, class E1, class T2, class E2>
+ friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
+
+ // Comparison with unexpected<E>
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
+
+ // Specialized algorithms
+ template<class T1, class E1>
+ friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+ std::variant<value_type, unexpected_type> var_;
+};
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return *x == *y;
+}
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+ return !(x == y);
+}
+
+// Comparison with unexpected<E>
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
+ return !x.has_value() && (x.error() == y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
+ return !y.has_value() && (x.value() == y.error());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
+ return x.has_value() || (x.error() != y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
+ return y.has_value() || (x.value() != y.error());
+}
+
+template<class E>
+class _NODISCARD_ expected<void, E> {
+ public:
+ using value_type = void;
+ using error_type = E;
+ using unexpected_type = unexpected<E>;
+
+ // constructors
+ constexpr expected() = default;
+ constexpr expected(const expected& rhs) = default;
+ constexpr expected(expected&& rhs) noexcept = default;
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(const expected<U, G>& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const expected<U, G>& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ std::is_convertible_v<const G&&, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(expected<U, G>&& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ !std::is_convertible_v<const G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(expected<U, G>&& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, e.value()) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, E(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ std::is_convertible_v<G&&, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ !std::is_convertible_v<G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+ template<class... Args _ENABLE_IF(
+ sizeof...(Args) == 0
+ )>
+ constexpr explicit expected(std::in_place_t, Args&&...) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<E, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, Args&&... args)
+ : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+ : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+ // destructor
+ ~expected() = default;
+
+ // assignment
+ // Note: SFNAIE doesn't work here because assignment operator should be
+ // non-template. We could workaround this by defining a templated parent class
+ // having the assignment operator. This incomplete implementation however
+ // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+ // assignable. The copy assignment will fail by the underlying std::variant
+ // anyway though the error message won't be clear.
+ expected& operator=(const expected& rhs) = default;
+
+ // Note for SFNAIE above applies to here as well
+ expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
+
+ template<class G = E>
+ expected& operator=(const unexpected<G>& rhs) {
+ var_ = rhs;
+ return *this;
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_nothrow_move_constructible_v<G> &&
+ std::is_move_assignable_v<G>
+ )>
+ expected& operator=(unexpected<G>&& rhs) {
+ var_ = std::move(rhs);
+ return *this;
+ }
+
+ // modifiers
+ void emplace() {
+ var_ = std::monostate();
+ }
+
+ // swap
+ template<typename = std::enable_if_t<
+ std::is_swappable_v<E>>
+ >
+ void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
+ var_.swap(rhs.var_);
+ }
+
+ // observers
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ constexpr bool has_value() const noexcept { return var_.index() == 0; }
+ constexpr bool ok() const noexcept { return has_value(); }
+
+ constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
+
+ constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+ constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+ constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+ constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+ // expected equality operators
+ template<class E1, class E2>
+ friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
+
+ // Specialized algorithms
+ template<class T1, class E1>
+ friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+ std::variant<std::monostate, unexpected_type> var_;
+};
+
+template<class E1, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return true;
+}
+
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
+}
+
+template<class E1, class T2, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
+}
+
+template<class E>
+class unexpected {
+ public:
+ // constructors
+ constexpr unexpected(const unexpected&) = default;
+ constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
+
+ template <class Err = E _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
+ constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+ : val_(il, std::forward<Args>(args)...) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ std::is_convertible_v<Err, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr unexpected(const unexpected<Err>& rhs)
+ : val_(rhs.value()) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ !std::is_convertible_v<Err, E> /* explicit */
+ )>
+ constexpr explicit unexpected(const unexpected<Err>& rhs)
+ : val_(E(rhs.value())) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ std::is_convertible_v<Err, E> /* non-explicit */
+ )>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ constexpr unexpected(unexpected<Err>&& rhs)
+ : val_(std::move(rhs.value())) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ !std::is_convertible_v<Err, E> /* explicit */
+ )>
+ constexpr explicit unexpected(unexpected<Err>&& rhs)
+ : val_(E(std::move(rhs.value()))) {}
+
+ // assignment
+ constexpr unexpected& operator=(const unexpected&) = default;
+ constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
+ default;
+ template<class Err = E>
+ constexpr unexpected& operator=(const unexpected<Err>& rhs) {
+ val_ = rhs.value();
+ return *this;
+ }
+ template<class Err = E>
+ constexpr unexpected& operator=(unexpected<Err>&& rhs) {
+ val_ = std::forward<E>(rhs.value());
+ return *this;
+ }
+
+ // observer
+ constexpr const E& value() const& noexcept { return val_; }
+ constexpr E& value() & noexcept { return val_; }
+ constexpr const E&& value() const&& noexcept { return std::move(val_); }
+ constexpr E&& value() && noexcept { return std::move(val_); }
+
+ void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+ std::swap(val_, other.val_);
+ }
+
+ template<class E1, class E2>
+ friend constexpr bool
+ operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
+ template<class E1, class E2>
+ friend constexpr bool
+ operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
+
+ template<class E1>
+ friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
+
+ private:
+ E val_;
+};
+
+template<class E1, class E2>
+constexpr bool
+operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+ return e1.value() == e2.value();
+}
+
+template<class E1, class E2>
+constexpr bool
+operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+ return e1.value() != e2.value();
+}
+
+template<class E1>
+void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
+ x.swap(y);
+}
+
+// TODO: bad_expected_access class
+
+#undef _ENABLE_IF
+#undef _NODISCARD_
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
new file mode 100644
index 000000000..c62256277
--- /dev/null
+++ b/base/include/android-base/file.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
+
+#if !defined(_WIN32) && !defined(O_BINARY)
+/** Windows needs O_BINARY, but Unix never mangles line endings. */
+#define O_BINARY 0
+#endif
+
+#if defined(_WIN32) && !defined(O_CLOEXEC)
+/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */
+#define O_CLOEXEC O_NOINHERIT
+#endif
+
+class TemporaryFile {
+ public:
+ TemporaryFile();
+ explicit TemporaryFile(const std::string& tmp_dir);
+ ~TemporaryFile();
+
+ // Release the ownership of fd, caller is reponsible for closing the
+ // fd or stream properly.
+ int release();
+ // Don't remove the temporary file in the destructor.
+ void DoNotRemove() { remove_file_ = false; }
+
+ int fd;
+ char path[1024];
+
+ private:
+ void init(const std::string& tmp_dir);
+
+ bool remove_file_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+ TemporaryDir();
+ ~TemporaryDir();
+ // Don't remove the temporary dir in the destructor.
+ void DoNotRemove() { remove_dir_and_contents_ = false; }
+
+ char path[1024];
+
+ private:
+ bool init(const std::string& tmp_dir);
+
+ bool remove_dir_and_contents_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
+};
+
+namespace android {
+namespace base {
+
+bool ReadFdToString(borrowed_fd fd, std::string* content);
+bool ReadFileToString(const std::string& path, std::string* content,
+ bool follow_symlinks = false);
+
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ bool follow_symlinks = false);
+bool WriteStringToFd(const std::string& content, borrowed_fd fd);
+
+#if !defined(_WIN32)
+bool WriteStringToFile(const std::string& content, const std::string& path,
+ mode_t mode, uid_t owner, gid_t group,
+ bool follow_symlinks = false);
+#endif
+
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
+
+// Reads `byte_count` bytes from the file descriptor at the specified offset.
+// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
+//
+// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
+// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
+// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
+// same function, but concurrently seeking or reading incrementally can lead to unexpected
+// behavior.
+bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
+
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
+
+bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
+
+#if !defined(_WIN32)
+bool Realpath(const std::string& path, std::string* result);
+bool Readlink(const std::string& path, std::string* result);
+#endif
+
+std::string GetExecutablePath();
+std::string GetExecutableDirectory();
+
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string Basename(const std::string& path);
+std::string Dirname(const std::string& path);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
new file mode 100644
index 000000000..330040d8a
--- /dev/null
+++ b/base/include/android-base/format.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
+// It is accessed through its normal fmt:: namespace.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#include <fmt/chrono.h>
+#pragma clang diagnostic pop
+#include <fmt/core.h>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
+#include <fmt/printf.h>
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
new file mode 100644
index 000000000..26827fb83
--- /dev/null
+++ b/base/include/android-base/logging.h
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+//
+// Google-style C++ logging.
+//
+
+// This header provides a C++ stream interface to logging.
+//
+// To log:
+//
+// LOG(INFO) << "Some text; " << some_value;
+//
+// Replace `INFO` with any severity from `enum LogSeverity`.
+//
+// To log the result of a failed function and include the string
+// representation of `errno` at the end:
+//
+// PLOG(ERROR) << "Write failed";
+//
+// The output will be something like `Write failed: I/O error`.
+// Remember this as 'P' as in perror(3).
+//
+// To output your own types, simply implement operator<< as normal.
+//
+// By default, output goes to logcat on Android and stderr on the host.
+// A process can use `SetLogger` to decide where all logging goes.
+// Implementations are provided for logcat, stderr, and dmesg.
+//
+// By default, the process' name is used as the log tag.
+// Code can choose a specific log tag by defining LOG_TAG
+// before including this header.
+
+// This header also provides assertions:
+//
+// CHECK(must_be_true);
+// CHECK_EQ(a, b) << z_is_interesting_too;
+
+// NOTE: For Windows, you must include logging.h after windows.h to allow the
+// following code to suppress the evil ERROR macro:
+#ifdef _WIN32
+// windows.h includes wingdi.h which defines an evil macro ERROR.
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include <functional>
+#include <memory>
+#include <ostream>
+
+#include "android-base/errno_restorer.h"
+#include "android-base/macros.h"
+
+// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
+#ifdef _LOG_TAG_INTERNAL
+#error "_LOG_TAG_INTERNAL must not be defined"
+#endif
+#ifdef LOG_TAG
+#define _LOG_TAG_INTERNAL LOG_TAG
+#else
+#define _LOG_TAG_INTERNAL nullptr
+#endif
+
+namespace android {
+namespace base {
+
+enum LogSeverity {
+ VERBOSE,
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR,
+ FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL.
+ FATAL,
+};
+
+enum LogId {
+ DEFAULT,
+ MAIN,
+ SYSTEM,
+ RADIO,
+ CRASH,
+};
+
+using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*,
+ unsigned int, const char*)>;
+using AbortFunction = std::function<void(const char*)>;
+
+// Loggers for use with InitLogging/SetLogger.
+
+// Log to the kernel log (dmesg).
+void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log to stderr in the full logcat format (with pid/tid/time/tag details).
+void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log just the message to stdout/stderr (without pid/tid/time/tag details).
+// The choice of stdout versus stderr is based on the severity.
+// Errors are also prefixed by the program name (as with err(3)/error(3)).
+// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
+void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+
+void DefaultAborter(const char* abort_message);
+
+void SetDefaultTag(const std::string& tag);
+
+// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
+// threads from writing to logd between sending each chunk, so other threads may interleave their
+// messages. If preventing interleaving is required, then a custom logger that takes a lock before
+// calling this logger should be provided.
+class LogdLogger {
+ public:
+ explicit LogdLogger(LogId default_log_id = android::base::MAIN);
+
+ void operator()(LogId, LogSeverity, const char* tag, const char* file,
+ unsigned int line, const char* message);
+
+ private:
+ LogId default_log_id_;
+};
+
+// Configure logging based on ANDROID_LOG_TAGS environment variable.
+// We need to parse a string that looks like
+//
+// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+//
+// The tag (or '*' for the global level) comes first, followed by a colon and a
+// letter indicating the minimum priority level we're expected to log. This can
+// be used to reveal or conceal logs with specific tags.
+#ifdef __ANDROID__
+#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
+#else
+#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
+#endif
+void InitLogging(char* argv[],
+ LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,
+ AbortFunction&& aborter = DefaultAborter);
+#undef INIT_LOGGING_DEFAULT_LOGGER
+
+// Replace the current logger.
+void SetLogger(LogFunction&& logger);
+
+// Replace the current aborter.
+void SetAborter(AbortFunction&& aborter);
+
+// A helper macro that produces an expression that accepts both a qualified name and an
+// unqualified name for a LogSeverity, and returns a LogSeverity value.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define SEVERITY_LAMBDA(severity) ([&]() { \
+ using ::android::base::VERBOSE; \
+ using ::android::base::DEBUG; \
+ using ::android::base::INFO; \
+ using ::android::base::WARNING; \
+ using ::android::base::ERROR; \
+ using ::android::base::FATAL_WITHOUT_ABORT; \
+ using ::android::base::FATAL; \
+ return (severity); }())
+
+#ifdef __clang_analyzer__
+// Clang's static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;; abort())
+
+struct LogAbortAfterFullExpr {
+ ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); }
+ explicit operator bool() const { return false; }
+};
+// Provides an expression that evaluates to the truthiness of `x`, automatically
+// aborting if `c` is true.
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x))
+// Note to the static analyzer that we always execute FATAL logs in practice.
+#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL)
+#else
+#define ABORT_AFTER_LOG_FATAL
+#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x)
+#define MUST_LOG_MESSAGE(severity) false
+#endif
+#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x)
+
+// Defines whether the given severity will be logged or silently swallowed.
+#define WOULD_LOG(severity) \
+ (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \
+ MUST_LOG_MESSAGE(severity))
+
+// Get an ostream that can be used for logging at the given severity and to the default
+// destination.
+//
+// Notes:
+// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter
+// usage manually.
+// 2) This does not save and restore errno.
+#define LOG_STREAM(severity) \
+ ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, \
+ -1) \
+ .stream()
+
+// Logs a message to logcat on Android otherwise to stderr. If the severity is
+// FATAL it also causes an abort. For example:
+//
+// LOG(FATAL) << "We didn't expect to reach here";
+#define LOG(severity) LOGGING_PREAMBLE(severity) && LOG_STREAM(severity)
+
+// Checks if we want to log something, and sets up appropriate RAII objects if
+// so.
+// Note: DO NOT USE DIRECTLY. This is an implementation detail.
+#define LOGGING_PREAMBLE(severity) \
+ (WOULD_LOG(severity) && \
+ ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \
+ ::android::base::ErrnoRestorer())
+
+// A variant of LOG that also logs the current errno value. To be used when
+// library calls fail.
+#define PLOG(severity) \
+ LOGGING_PREAMBLE(severity) && \
+ ::android::base::LogMessage(__FILE__, __LINE__, SEVERITY_LAMBDA(severity), \
+ _LOG_TAG_INTERNAL, errno) \
+ .stream()
+
+// Marker that code is yet to be implemented.
+#define UNIMPLEMENTED(level) \
+ LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+// Check whether condition x holds and LOG(FATAL) if not. The value of the
+// expression x is only evaluated once. Extra logging can be appended using <<
+// after. For example:
+//
+// CHECK(false == true) results in a log message of
+// "Check failed: false == true".
+#define CHECK(x) \
+ LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, \
+ -1) \
+ .stream() \
+ << "Check failed: " #x << " "
+
+// clang-format off
+// Helper for CHECK_xx(x,y) macros.
+#define CHECK_OP(LHS, RHS, OP) \
+ for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
+ UNLIKELY(!(_values.lhs OP _values.rhs)); \
+ /* empty */) \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+ .stream() \
+ << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
+ << ", " #RHS "=" << _values.rhs << ") "
+// clang-format on
+
+// Check whether a condition holds between x and y, LOG(FATAL) if not. The value
+// of the expressions x and y is evaluated once. Extra logging can be appended
+// using << after. For example:
+//
+// CHECK_NE(0 == 1, false) results in
+// "Check failed: false != false (0==1=false, false=false) ".
+#define CHECK_EQ(x, y) CHECK_OP(x, y, == )
+#define CHECK_NE(x, y) CHECK_OP(x, y, != )
+#define CHECK_LE(x, y) CHECK_OP(x, y, <= )
+#define CHECK_LT(x, y) CHECK_OP(x, y, < )
+#define CHECK_GE(x, y) CHECK_OP(x, y, >= )
+#define CHECK_GT(x, y) CHECK_OP(x, y, > )
+
+// clang-format off
+// Helper for CHECK_STRxx(s1,s2) macros.
+#define CHECK_STROP(s1, s2, sense) \
+ while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
+ ABORT_AFTER_LOG_FATAL \
+ ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::FATAL, \
+ _LOG_TAG_INTERNAL, -1) \
+ .stream() \
+ << "Check failed: " << "\"" << (s1) << "\"" \
+ << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
+// clang-format on
+
+// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+// Perform the pthread function call(args), LOG(FATAL) on error.
+#define CHECK_PTHREAD_CALL(call, args, what) \
+ do { \
+ int rc = call args; \
+ if (rc != 0) { \
+ errno = rc; \
+ ABORT_AFTER_LOG_FATAL \
+ PLOG(FATAL) << #call << " failed for " << (what); \
+ } \
+ } while (false)
+
+// CHECK that can be used in a constexpr function. For example:
+//
+// constexpr int half(int n) {
+// return
+// DCHECK_CONSTEXPR(n >= 0, , 0)
+// CHECK_CONSTEXPR((n & 1) == 0),
+// << "Extra debugging output: n = " << n, 0)
+// n / 2;
+// }
+#define CHECK_CONSTEXPR(x, out, dummy) \
+ (UNLIKELY(!(x))) \
+ ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \
+ :
+
+// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally
+// CHECK should be used unless profiling identifies a CHECK as being in
+// performance critical code.
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
+static constexpr bool kEnableDChecks = false;
+#else
+static constexpr bool kEnableDChecks = true;
+#endif
+
+#define DCHECK(x) \
+ if (::android::base::kEnableDChecks) CHECK(x)
+#define DCHECK_EQ(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_EQ(x, y)
+#define DCHECK_NE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_NE(x, y)
+#define DCHECK_LE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LE(x, y)
+#define DCHECK_LT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_LT(x, y)
+#define DCHECK_GE(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GE(x, y)
+#define DCHECK_GT(x, y) \
+ if (::android::base::kEnableDChecks) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) \
+ if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2)
+#if defined(NDEBUG) && !defined(__clang_analyzer__)
+#define DCHECK_CONSTEXPR(x, out, dummy)
+#else
+#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy)
+#endif
+
+// Temporary class created to evaluate the LHS and RHS, used with
+// MakeEagerEvaluator to infer the types of LHS and RHS.
+template <typename LHS, typename RHS>
+struct EagerEvaluator {
+ constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
+ }
+ LHS lhs;
+ RHS rhs;
+};
+
+// Helper function for CHECK_xx.
+template <typename LHS, typename RHS>
+constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+ return EagerEvaluator<LHS, RHS>(lhs, rhs);
+}
+
+// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated
+// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on
+// signed/unsigned warnings to protect you against combinations not explicitly
+// listed below.
+#define EAGER_PTR_EVALUATOR(T1, T2) \
+ template <> \
+ struct EagerEvaluator<T1, T2> { \
+ EagerEvaluator(T1 l, T2 r) \
+ : lhs(reinterpret_cast<const void*>(l)), \
+ rhs(reinterpret_cast<const void*>(r)) { \
+ } \
+ const void* lhs; \
+ const void* rhs; \
+ }
+EAGER_PTR_EVALUATOR(const char*, const char*);
+EAGER_PTR_EVALUATOR(const char*, char*);
+EAGER_PTR_EVALUATOR(char*, const char*);
+EAGER_PTR_EVALUATOR(char*, char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
+EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
+EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(const signed char*, signed char*);
+EAGER_PTR_EVALUATOR(signed char*, const signed char*);
+EAGER_PTR_EVALUATOR(signed char*, signed char*);
+
+// Data for the log message, not stored in LogMessage to avoid increasing the
+// stack size.
+class LogMessageData;
+
+// A LogMessage is a temporarily scoped object used by LOG and the unlikely part
+// of a CHECK. The destructor will abort if the severity is FATAL.
+class LogMessage {
+ public:
+ // LogId has been deprecated, but this constructor must exist for prebuilts.
+ LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, const char* tag,
+ int error);
+ LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, int error);
+
+ ~LogMessage();
+
+ // Returns the stream associated with the message, the LogMessage performs
+ // output when it goes out of scope.
+ std::ostream& stream();
+
+ // The routine that performs the actual logging.
+ static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ const char* msg);
+
+ private:
+ const std::unique_ptr<LogMessageData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Get the minimum severity level for logging.
+LogSeverity GetMinimumLogSeverity();
+
+// Set the minimum severity level for logging, returning the old severity.
+LogSeverity SetMinimumLogSeverity(LogSeverity new_severity);
+
+// Return whether or not a log message with the associated tag should be logged.
+bool ShouldLog(LogSeverity severity, const char* tag);
+
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
+} // namespace base
+} // namespace android
+
+namespace std { // NOLINT(cert-dcl58-cpp)
+
+// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
+//
+// Note: for this to work, we need to have this in a namespace.
+// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
+// diagnose_if.
+// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
+// Note: a not-recommended alternative is to let Clang ignore the warning by adding
+// -Wno-user-defined-warnings to CPPFLAGS.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#define OSTREAM_STRING_POINTER_USAGE_WARNING \
+ __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
+inline OSTREAM_STRING_POINTER_USAGE_WARNING
+std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) {
+ return stream << static_cast<const void*>(string_pointer);
+}
+#pragma clang diagnostic pop
+
+} // namespace std
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
new file mode 100644
index 000000000..5abf5141f
--- /dev/null
+++ b/base/include/android-base/macros.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h> // for size_t
+#include <unistd.h> // for TEMP_FAILURE_RETRY
+
+#include <utility>
+
+// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ decltype(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
+#endif
+
+// A macro to disallow the copy constructor and operator= functions
+// This must be placed in the private: declarations for a class.
+//
+// For disallowing only assign or copy, delete the relevant operator or
+// constructor, for example:
+// void operator=(const TypeName&) = delete;
+// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
+// semantically, one should either use disallow both or neither. Try to
+// avoid these in new code.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting)
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
+
+// Changing this definition will cause you a lot of pain. A majority of
+// vendor code defines LIKELY and UNLIKELY this way, and includes
+// this header through an indirect path.
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+#define WARN_UNUSED __attribute__((warn_unused_result))
+
+// A deprecated function to call to create a false use of the parameter, for
+// example:
+// int foo(int x) { UNUSED(x); return 10; }
+// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED.
+template <typename... T>
+void UNUSED(const T&...) {
+}
+
+// An attribute to place on a parameter to a function, for example:
+// int foo(int x ATTRIBUTE_UNUSED) { return 10; }
+// to avoid compiler warnings.
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels:
+// switch (x) {
+// case 40:
+// case 41:
+// if (truth_is_out_there) {
+// ++x;
+// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in
+// // comments.
+// } else {
+// return x;
+// }
+// case 42:
+// ...
+//
+// As shown in the example above, the FALLTHROUGH_INTENDED macro should be
+// followed by a semicolon. It is designed to mimic control-flow statements
+// like 'break;', so it can be placed in most places where 'break;' can, but
+// only if there are no statements on the execution path between it and the
+// next switch label.
+//
+// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+// [[clang::fallthrough]] attribute, which is analysed when performing switch
+// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+// documentation on language extensions for details:
+// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
+//
+// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
+// effect on diagnostics.
+//
+// In either case this macro has no effect on runtime behavior and performance
+// of code.
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
+#endif
+
+// Current ABI string
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#endif
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
new file mode 100644
index 000000000..8c37f4305
--- /dev/null
+++ b/base/include/android-base/mapped_file.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <memory>
+
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+#define PROT_READ 1
+#define PROT_WRITE 2
+using os_handle = HANDLE;
+#else
+#include <sys/mman.h>
+using os_handle = int;
+#endif
+
+namespace android {
+namespace base {
+
+/**
+ * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
+ */
+class MappedFile {
+ public:
+ /**
+ * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives,
+ * `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
+ * will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
+ */
+ static std::unique_ptr<MappedFile> FromFd(borrowed_fd fd, off64_t offset, size_t length,
+ int prot);
+
+ /**
+ * Same thing, but using the raw OS file handle instead of a CRT wrapper.
+ */
+ static std::unique_ptr<MappedFile> FromOsHandle(os_handle h, off64_t offset, size_t length,
+ int prot);
+
+ /**
+ * Removes the mapping.
+ */
+ ~MappedFile();
+
+ /**
+ * Not copyable but movable.
+ */
+ MappedFile(MappedFile&& other);
+ MappedFile& operator=(MappedFile&& other);
+
+ char* data() const { return base_ + offset_; }
+ size_t size() const { return size_; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
+
+ void Close();
+
+ char* base_;
+ size_t size_;
+
+ size_t offset_;
+
+#if defined(_WIN32)
+ MappedFile(char* base, size_t size, size_t offset, HANDLE handle)
+ : base_(base), size_(size), offset_(offset), handle_(handle) {}
+ HANDLE handle_;
+#else
+ MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {}
+#endif
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
new file mode 100644
index 000000000..0277a034e
--- /dev/null
+++ b/base/include/android-base/memory.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+namespace base {
+
+// Use memcpy for access to unaligned data on targets with alignment
+// restrictions. The compiler will generate appropriate code to access these
+// structures without generating alignment exceptions.
+template <typename T>
+static inline T get_unaligned(const void* address) {
+ T result;
+ memcpy(&result, address, sizeof(T));
+ return result;
+}
+
+template <typename T>
+static inline void put_unaligned(void* address, T v) {
+ memcpy(address, &v, sizeof(T));
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/no_destructor.h b/base/include/android-base/no_destructor.h
new file mode 100644
index 000000000..ce0dc9f2a
--- /dev/null
+++ b/base/include/android-base/no_destructor.h
@@ -0,0 +1,94 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utility>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+// A wrapper that makes it easy to create an object of type T with static
+// storage duration that:
+// - is only constructed on first access
+// - never invokes the destructor
+// in order to satisfy the styleguide ban on global constructors and
+// destructors.
+//
+// Runtime constant example:
+// const std::string& GetLineSeparator() {
+// // Forwards to std::string(size_t, char, const Allocator&) constructor.
+// static const base::NoDestructor<std::string> s(5, '-');
+// return *s;
+// }
+//
+// More complex initialization with a lambda:
+// const std::string& GetSessionNonce() {
+// static const base::NoDestructor<std::string> nonce([] {
+// std::string s(16);
+// crypto::RandString(s.data(), s.size());
+// return s;
+// }());
+// return *nonce;
+// }
+//
+// NoDestructor<T> stores the object inline, so it also avoids a pointer
+// indirection and a malloc. Also note that since C++11 static local variable
+// initialization is thread-safe and so is this pattern. Code should prefer to
+// use NoDestructor<T> over:
+// - A function scoped static T* or T& that is dynamically initialized.
+// - A global base::LazyInstance<T>.
+//
+// Note that since the destructor is never run, this *will* leak memory if used
+// as a stack or member variable. Furthermore, a NoDestructor<T> should never
+// have global scope as that may require a static initializer.
+template <typename T>
+class NoDestructor {
+ public:
+ // Not constexpr; just write static constexpr T x = ...; if the value should
+ // be a constexpr.
+ template <typename... Args>
+ explicit NoDestructor(Args&&... args) {
+ new (storage_) T(std::forward<Args>(args)...);
+ }
+
+ // Allows copy and move construction of the contained type, to allow
+ // construction from an initializer list, e.g. for std::vector.
+ explicit NoDestructor(const T& x) { new (storage_) T(x); }
+ explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
+
+ NoDestructor(const NoDestructor&) = delete;
+ NoDestructor& operator=(const NoDestructor&) = delete;
+
+ ~NoDestructor() = default;
+
+ const T& operator*() const { return *get(); }
+ T& operator*() { return *get(); }
+
+ const T* operator->() const { return get(); }
+ T* operator->() { return get(); }
+
+ const T* get() const { return reinterpret_cast<const T*>(storage_); }
+ T* get() { return reinterpret_cast<T*>(storage_); }
+
+ private:
+ alignas(T) char storage_[sizeof(T)];
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/off64_t.h b/base/include/android-base/off64_t.h
new file mode 100644
index 000000000..e6b71b81e
--- /dev/null
+++ b/base/include/android-base/off64_t.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if defined(__APPLE__)
+/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
diff --git a/base/include/android-base/parsebool.h b/base/include/android-base/parsebool.h
new file mode 100644
index 000000000..b2bd0217e
--- /dev/null
+++ b/base/include/android-base/parsebool.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string_view>
+
+namespace android {
+namespace base {
+
+// Parse the given string as yes or no inactivation of some sort. Return one of the
+// ParseBoolResult enumeration values.
+//
+// The following values parse as true:
+//
+// 1
+// on
+// true
+// y
+// yes
+//
+//
+// The following values parse as false:
+//
+// 0
+// false
+// n
+// no
+// off
+//
+// Anything else is a parse error.
+//
+// The purpose of this function is to have a single canonical parser for yes-or-no indications
+// throughout the system.
+
+enum class ParseBoolResult {
+ kError,
+ kFalse,
+ kTrue,
+};
+
+ParseBoolResult ParseBool(std::string_view s);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
new file mode 100644
index 000000000..ccffba232
--- /dev/null
+++ b/base/include/android-base/parsedouble.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <limits>
+#include <string>
+
+namespace android {
+namespace base {
+
+// Parse floating value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+template <typename T, T (*strtox)(const char* str, char** endptr)>
+static inline bool ParseFloatingPoint(const char* s, T* out, T min, T max) {
+ errno = 0;
+ char* end;
+ T result = strtox(s, &end);
+ if (errno != 0 || s == end || *end != '\0') {
+ return false;
+ }
+ if (result < min || max < result) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = result;
+ }
+ return true;
+}
+
+// Parse double value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseDouble(const char* s, double* out,
+ double min = std::numeric_limits<double>::lowest(),
+ double max = std::numeric_limits<double>::max()) {
+ return ParseFloatingPoint<double, strtod>(s, out, min, max);
+}
+static inline bool ParseDouble(const std::string& s, double* out,
+ double min = std::numeric_limits<double>::lowest(),
+ double max = std::numeric_limits<double>::max()) {
+ return ParseFloatingPoint<double, strtod>(s.c_str(), out, min, max);
+}
+
+// Parse float value in the string 's' and sets 'out' to that value if it exists.
+// Optionally allows the caller to define a 'min' and 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+static inline bool ParseFloat(const char* s, float* out,
+ float min = std::numeric_limits<float>::lowest(),
+ float max = std::numeric_limits<float>::max()) {
+ return ParseFloatingPoint<float, strtof>(s, out, min, max);
+}
+static inline bool ParseFloat(const std::string& s, float* out,
+ float min = std::numeric_limits<float>::lowest(),
+ float max = std::numeric_limits<float>::max()) {
+ return ParseFloatingPoint<float, strtof>(s.c_str(), out, min, max);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
new file mode 100644
index 000000000..be8b97b78
--- /dev/null
+++ b/base/include/android-base/parseint.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <limits>
+#include <string>
+#include <type_traits>
+
+namespace android {
+namespace base {
+
+// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value if it is specified. Optionally allows the caller to define
+// a 'max' beyond which otherwise valid values will be rejected. Returns boolean
+// success; 'out' is untouched if parsing fails.
+template <typename T>
+bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
+ bool allow_suffixes = false) {
+ static_assert(std::is_unsigned<T>::value, "ParseUint can only be used with unsigned types");
+ while (isspace(*s)) {
+ s++;
+ }
+
+ if (s[0] == '-') {
+ errno = EINVAL;
+ return false;
+ }
+
+ int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
+ errno = 0;
+ char* end;
+ unsigned long long int result = strtoull(s, &end, base);
+ if (errno != 0) return false;
+ if (end == s) {
+ errno = EINVAL;
+ return false;
+ }
+ if (*end != '\0') {
+ const char* suffixes = "bkmgtpe";
+ const char* suffix;
+ if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
+ __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
+ errno = EINVAL;
+ return false;
+ }
+ }
+ if (max < result) {
+ errno = ERANGE;
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<T>(result);
+ }
+ return true;
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
+ bool allow_suffixes = false) {
+ return ParseUint(s.c_str(), out, max, allow_suffixes);
+}
+
+template <typename T>
+bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
+ return ParseUint(s, out, max, true);
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
+ return ParseByteCount(s.c_str(), out, max);
+}
+
+// Parses the signed decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value if it is specified. Optionally allows the caller to define
+// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns
+// boolean success; 'out' is untouched if parsing fails.
+template <typename T>
+bool ParseInt(const char* s, T* out,
+ T min = std::numeric_limits<T>::min(),
+ T max = std::numeric_limits<T>::max()) {
+ static_assert(std::is_signed<T>::value, "ParseInt can only be used with signed types");
+ while (isspace(*s)) {
+ s++;
+ }
+
+ int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
+ errno = 0;
+ char* end;
+ long long int result = strtoll(s, &end, base);
+ if (errno != 0) {
+ return false;
+ }
+ if (s == end || *end != '\0') {
+ errno = EINVAL;
+ return false;
+ }
+ if (result < min || max < result) {
+ errno = ERANGE;
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<T>(result);
+ }
+ return true;
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseInt(const std::string& s, T* out,
+ T min = std::numeric_limits<T>::min(),
+ T max = std::numeric_limits<T>::max()) {
+ return ParseInt(s.c_str(), out, min, max);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
new file mode 100644
index 000000000..47f8b5f6e
--- /dev/null
+++ b/base/include/android-base/parsenetaddress.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace base {
+
+// Parses |address| into |host| and |port|.
+//
+// If |address| doesn't contain a port number, the default value is taken from
+// |port|. If |canonical_address| is non-null it will be set to "host:port" or
+// "[host]:port" as appropriate.
+//
+// On failure, returns false and fills |error|.
+bool ParseNetAddress(const std::string& address, std::string* host, int* port,
+ std::string* canonical_address, std::string* error);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/process.h b/base/include/android-base/process.h
new file mode 100644
index 000000000..69ed3fba5
--- /dev/null
+++ b/base/include/android-base/process.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <iterator>
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace base {
+
+class AllPids {
+ class PidIterator {
+ public:
+ PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
+ PidIterator& operator++() {
+ Increment();
+ return *this;
+ }
+ bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
+ bool operator!=(const PidIterator& other) const { return !(*this == other); }
+ long operator*() const { return pid_; }
+ // iterator traits
+ using difference_type = pid_t;
+ using value_type = pid_t;
+ using pointer = const pid_t*;
+ using reference = const pid_t&;
+ using iterator_category = std::input_iterator_tag;
+
+ private:
+ void Increment();
+
+ pid_t pid_ = -1;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_;
+ };
+
+ public:
+ PidIterator begin() { return opendir("/proc"); }
+ PidIterator end() { return nullptr; }
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
new file mode 100644
index 000000000..49f1f3184
--- /dev/null
+++ b/base/include/android-base/properties.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+
+#include <chrono>
+#include <limits>
+#include <optional>
+#include <string>
+
+struct prop_info;
+
+namespace android {
+namespace base {
+
+// Returns the current value of the system property `key`,
+// or `default_value` if the property is empty or doesn't exist.
+std::string GetProperty(const std::string& key, const std::string& default_value);
+
+// Returns true if the system property `key` has the value "1", "y", "yes", "on", or "true",
+// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
+bool GetBoolProperty(const std::string& key, bool default_value);
+
+// Returns the signed integer corresponding to the system property `key`.
+// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
+// the optional bounds, returns `default_value`.
+template <typename T> T GetIntProperty(const std::string& key,
+ T default_value,
+ T min = std::numeric_limits<T>::min(),
+ T max = std::numeric_limits<T>::max());
+
+// Returns the unsigned integer corresponding to the system property `key`.
+// If the property is empty, doesn't exist, doesn't have an integer value, or is outside
+// the optional bound, returns `default_value`.
+template <typename T> T GetUintProperty(const std::string& key,
+ T default_value,
+ T max = std::numeric_limits<T>::max());
+
+// Sets the system property `key` to `value`.
+bool SetProperty(const std::string& key, const std::string& value);
+
+// Waits for the system property `key` to have the value `expected_value`.
+// Times out after `relative_timeout`.
+// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
+
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+ std::chrono::milliseconds::max());
+#endif
+
+#if defined(__BIONIC__) && __cplusplus >= 201703L
+// Cached system property lookup. For code that needs to read the same property multiple times,
+// this class helps optimize those lookups.
+class CachedProperty {
+ public:
+ explicit CachedProperty(const char* property_name);
+
+ // Returns the current value of the underlying system property as cheaply as possible.
+ // The returned pointer is valid until the next call to Get. Because most callers are going
+ // to want to parse the string returned here and cached that as well, this function performs
+ // no locking, and is completely thread unsafe. It is the caller's responsibility to provide a
+ // lock for thread-safety.
+ //
+ // Note: *changed can be set to true even if the contents of the property remain the same.
+ const char* Get(bool* changed = nullptr);
+
+ private:
+ std::string property_name_;
+ const prop_info* prop_info_;
+ std::optional<uint32_t> cached_area_serial_;
+ std::optional<uint32_t> cached_property_serial_;
+ char cached_value_[92];
+ bool is_read_only_;
+ const char* read_only_property_;
+};
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
new file mode 100644
index 000000000..56a4f3e80
--- /dev/null
+++ b/base/include/android-base/result.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred. ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Result<void> is the correct return type for a function that either returns successfully or
+// returns an error value. Returning {} from a function that returns Result<void> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T. This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
+// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
+//
+// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
+// In this case, the string that the ResultError takes is passed through the stream normally, but
+// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
+// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
+// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
+// used, the errno of the last one is respected.
+//
+// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error. In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+// U output;
+// if (!SomeOtherCppFunction(input, &output)) {
+// return Errorf("SomeOtherCppFunction {} failed", input);
+// }
+// if (!c_api_function(output)) {
+// return ErrnoErrorf("c_api_function {} failed", output);
+// }
+// return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#pragma once
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+
+#include "android-base/expected.h"
+#include "android-base/format.h"
+
+namespace android {
+namespace base {
+
+struct ResultError {
+ template <typename T>
+ ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
+
+ template <typename T>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator android::base::expected<T, ResultError>() {
+ return android::base::unexpected(ResultError(message_, code_));
+ }
+
+ std::string message() const { return message_; }
+ int code() const { return code_; }
+
+ private:
+ std::string message_;
+ int code_;
+};
+
+inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
+ return lhs.message() == rhs.message() && lhs.code() == rhs.code();
+}
+
+inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+ os << t.message();
+ return os;
+}
+
+class Error {
+ public:
+ Error() : errno_(0), append_errno_(false) {}
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+ template <typename T>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator android::base::expected<T, ResultError>() {
+ return android::base::unexpected(ResultError(str(), errno_));
+ }
+
+ template <typename T>
+ Error& operator<<(T&& t) {
+ // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+ errno_ = t.code();
+ return (*this) << t.message();
+ }
+ int saved = errno;
+ ss_ << t;
+ errno = saved;
+ return *this;
+ }
+
+ const std::string str() const {
+ std::string str = ss_.str();
+ if (append_errno_) {
+ if (str.empty()) {
+ return strerror(errno_);
+ }
+ return std::move(str) + ": " + strerror(errno_);
+ }
+ return str;
+ }
+
+ Error(const Error&) = delete;
+ Error(Error&&) = delete;
+ Error& operator=(const Error&) = delete;
+ Error& operator=(Error&&) = delete;
+
+ template <typename T, typename... Args>
+ friend Error ErrorfImpl(const T&& fmt, const Args&... args);
+
+ template <typename T, typename... Args>
+ friend Error ErrnoErrorfImpl(const T&& fmt, const Args&... args);
+
+ private:
+ Error(bool append_errno, int errno_to_append, const std::string& message)
+ : errno_(errno_to_append), append_errno_(append_errno) {
+ (*this) << message;
+ }
+
+ std::stringstream ss_;
+ int errno_;
+ const bool append_errno_;
+};
+
+inline Error ErrnoError() {
+ return Error(errno);
+}
+
+inline int ErrorCode(int code) {
+ return code;
+}
+
+// Return the error code of the last ResultError object, if any.
+// Otherwise, return `code` as it is.
+template <typename T, typename... Args>
+inline int ErrorCode(int code, T&& t, const Args&... args) {
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+ return ErrorCode(t.code(), args...);
+ }
+ return ErrorCode(code, args...);
+}
+
+template <typename T, typename... Args>
+inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
+ return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
+}
+
+template <typename T, typename... Args>
+inline Error ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
+ return Error(true, errno, fmt::format(fmt, args...));
+}
+
+#define Errorf(fmt, ...) android::base::ErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
+#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
+
+template <typename T>
+using Result = android::base::expected<T, ResultError>;
+
+// Macros for testing the results of functions that return android::base::Result.
+// These also work with base::android::expected.
+
+#define CHECK_RESULT_OK(stmt) \
+ do { \
+ const auto& tmp = (stmt); \
+ CHECK(tmp.ok()) << tmp.error(); \
+ } while (0)
+
+#define ASSERT_RESULT_OK(stmt) \
+ do { \
+ const auto& tmp = (stmt); \
+ ASSERT_TRUE(tmp.ok()) << tmp.error(); \
+ } while (0)
+
+#define EXPECT_RESULT_OK(stmt) \
+ do { \
+ auto tmp = (stmt); \
+ EXPECT_TRUE(tmp.ok()) << tmp.error(); \
+ } while (0)
+
+// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 000000000..5a224d634
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utility> // for std::move, std::forward
+
+namespace android {
+namespace base {
+
+// ScopeGuard ensures that the specified functor is executed no matter how the
+// current scope exits.
+template <typename F>
+class ScopeGuard {
+ public:
+ ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
+
+ ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ template <typename Functor>
+ ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ ~ScopeGuard() {
+ if (active_) f_();
+ }
+
+ ScopeGuard() = delete;
+ ScopeGuard(const ScopeGuard&) = delete;
+ void operator=(const ScopeGuard&) = delete;
+ void operator=(ScopeGuard&& that) = delete;
+
+ void Disable() { active_ = false; }
+
+ bool active() const { return active_; }
+
+ private:
+ template <typename Functor>
+ friend class ScopeGuard;
+
+ F f_;
+ bool active_;
+};
+
+template <typename F>
+ScopeGuard<F> make_scope_guard(F&& f) {
+ return ScopeGuard<F>(std::forward<F>(f));
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
new file mode 100644
index 000000000..93c56afd7
--- /dev/null
+++ b/base/include/android-base/stringprintf.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdarg.h>
+#include <string>
+
+namespace android {
+namespace base {
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking.
+
+// Returns a string corresponding to printf-like formatting of the arguments.
+std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendF(std::string* dst, const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+ __attribute__((__format__(__printf__, 2, 0)));
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
new file mode 100644
index 000000000..14d534a3b
--- /dev/null
+++ b/base/include/android-base/strings.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace android {
+namespace base {
+
+// Splits a string into a vector of strings.
+//
+// The string is split at each occurrence of a character in delimiters.
+//
+// The empty string is not a valid delimiter list.
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters);
+
+// Trims whitespace off both ends of the given string.
+std::string Trim(const std::string& s);
+
+// Joins a container of things into a single string, using the given separator.
+template <typename ContainerT, typename SeparatorT>
+std::string Join(const ContainerT& things, SeparatorT separator) {
+ if (things.empty()) {
+ return "";
+ }
+
+ std::ostringstream result;
+ result << *things.begin();
+ for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+ result << separator << *it;
+ }
+ return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector<std::string>&, char);
+extern template std::string Join(const std::vector<const char*>&, char);
+extern template std::string Join(const std::vector<std::string>&, const std::string&);
+extern template std::string Join(const std::vector<const char*>&, const std::string&);
+
+// Tests whether 's' starts with 'prefix'.
+bool StartsWith(std::string_view s, std::string_view prefix);
+bool StartsWith(std::string_view s, char prefix);
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
+
+// Tests whether 's' ends with 'suffix'.
+bool EndsWith(std::string_view s, std::string_view suffix);
+bool EndsWith(std::string_view s, char suffix);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
+
+// Tests whether 'lhs' equals 'rhs', ignoring case.
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
+
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+ if (!StartsWith(*s, prefix)) return false;
+ s->remove_prefix(prefix.size());
+ return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+ if (!EndsWith(*s, suffix)) return false;
+ s->remove_suffix(suffix.size());
+ return true;
+}
+
+// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
+// there are matches if `all == true`.
+[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
+ std::string_view to, bool all);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
new file mode 100644
index 000000000..f3d7cb031
--- /dev/null
+++ b/base/include/android-base/test_utils.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <regex>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+
+class CapturedStdFd {
+ public:
+ CapturedStdFd(int std_fd);
+ ~CapturedStdFd();
+
+ std::string str();
+
+ void Start();
+ void Stop();
+ void Reset();
+
+ private:
+ int fd() const;
+
+ TemporaryFile temp_file_;
+ int std_fd_;
+ int old_fd_ = -1;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
+};
+
+class CapturedStderr : public CapturedStdFd {
+ public:
+ CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
+};
+
+class CapturedStdout : public CapturedStdFd {
+ public:
+ CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
+};
+
+#define ASSERT_MATCH(str, pattern) \
+ do { \
+ auto __s = (str); \
+ if (!std::regex_search(__s, std::regex((pattern)))) { \
+ FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
+ } \
+ } while (0)
+
+#define ASSERT_NOT_MATCH(str, pattern) \
+ do { \
+ auto __s = (str); \
+ if (std::regex_search(__s, std::regex((pattern)))) { \
+ FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
+ } \
+ } while (0)
+
+#define EXPECT_MATCH(str, pattern) \
+ do { \
+ auto __s = (str); \
+ if (!std::regex_search(__s, std::regex((pattern)))) { \
+ ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
+ } \
+ } while (0)
+
+#define EXPECT_NOT_MATCH(str, pattern) \
+ do { \
+ auto __s = (str); \
+ if (std::regex_search(__s, std::regex((pattern)))) { \
+ ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
+ } \
+ } while (0)
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
new file mode 100644
index 000000000..53fe6dae9
--- /dev/null
+++ b/base/include/android-base/thread_annotations.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+
+#define CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY \
+ THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define SHARED_CAPABILITY(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
+
+#define GUARDED_BY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+#define SHARED_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+#define SHARED_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+#define UNLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+#define SCOPED_LOCKABLE \
+ THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define LOCK_RETURNED(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+// std::mutex mutex;
+// std::condition_variable cv;
+// std::vector<int> vec GUARDED_BY(mutex);
+//
+// int pop() {
+// std::unique_lock lock(mutex);
+// ScopedLockAssertion lock_assertion(mutex);
+// cv.wait(lock, []() {
+// ScopedLockAssertion lock_assertion(mutex);
+// return !vec.empty();
+// });
+//
+// int result = vec.back();
+// vec.pop_back();
+// return result;
+// }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+ ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+ ~ScopedLockAssertion() RELEASE() {}
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
new file mode 100644
index 000000000..dba1fc620
--- /dev/null
+++ b/base/include/android-base/threads.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+namespace base {
+uint64_t GetThreadId();
+}
+} // namespace android
+
+#if defined(__GLIBC__)
+// bionic has this Linux-specifix call, but glibc doesn't.
+extern "C" int tgkill(int tgid, int tid, int sig);
+#endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
new file mode 100644
index 000000000..c4a0aad0f
--- /dev/null
+++ b/base/include/android-base/unique_fd.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// DO NOT INCLUDE OTHER LIBBASE HEADERS!
+// This file gets used in libbinder, and libbinder is used everywhere.
+// Including other headers from libbase frequently results in inclusion of
+// android-base/macros.h, which causes macro collisions.
+
+// Container for a file descriptor that automatically closes the descriptor as
+// it goes out of scope.
+//
+// unique_fd ufd(open("/some/path", "r"));
+// if (ufd.get() == -1) return error;
+//
+// // Do something useful, possibly including 'return'.
+//
+// return 0; // Descriptor is closed for you.
+//
+// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
+// you find this class if you're searching for one of those names.
+
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
+namespace android {
+namespace base {
+
+struct DefaultCloser {
+#if defined(__BIONIC__)
+ static void Tag(int fd, void* old_addr, void* new_addr) {
+ if (android_fdsan_exchange_owner_tag) {
+ uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+ reinterpret_cast<uint64_t>(old_addr));
+ uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+ reinterpret_cast<uint64_t>(new_addr));
+ android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+ }
+ }
+ static void Close(int fd, void* addr) {
+ if (android_fdsan_close_with_tag) {
+ uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+ reinterpret_cast<uint64_t>(addr));
+ android_fdsan_close_with_tag(fd, tag);
+ } else {
+ close(fd);
+ }
+ }
+#else
+ static void Close(int fd) {
+ // Even if close(2) fails with EINTR, the fd will have been closed.
+ // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
+ // else's fd.
+ // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ ::close(fd);
+ }
+#endif
+};
+
+template <typename Closer>
+class unique_fd_impl final {
+ public:
+ unique_fd_impl() {}
+
+ explicit unique_fd_impl(int fd) { reset(fd); }
+ ~unique_fd_impl() { reset(); }
+
+ unique_fd_impl(const unique_fd_impl&) = delete;
+ void operator=(const unique_fd_impl&) = delete;
+ unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
+ unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
+ int fd = s.fd_;
+ s.fd_ = -1;
+ reset(fd, &s);
+ return *this;
+ }
+
+ void reset(int new_value = -1) { reset(new_value, nullptr); }
+
+ int get() const { return fd_; }
+
+#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
+ // unique_fd's operator int is dangerous, but we have way too much code that
+ // depends on it, so make this opt-in at first.
+ operator int() const { return get(); } // NOLINT
+#endif
+
+ bool operator>=(int rhs) const { return get() >= rhs; }
+ bool operator<(int rhs) const { return get() < rhs; }
+ bool operator==(int rhs) const { return get() == rhs; }
+ bool operator!=(int rhs) const { return get() != rhs; }
+ bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); }
+ bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); }
+
+ // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
+ bool operator!() const = delete;
+
+ bool ok() const { return get() >= 0; }
+
+ int release() __attribute__((warn_unused_result)) {
+ tag(fd_, this, nullptr);
+ int ret = fd_;
+ fd_ = -1;
+ return ret;
+ }
+
+ private:
+ void reset(int new_value, void* previous_tag) {
+ int previous_errno = errno;
+
+ if (fd_ != -1) {
+ close(fd_, this);
+ }
+
+ fd_ = new_value;
+ if (new_value != -1) {
+ tag(new_value, previous_tag, this);
+ }
+
+ errno = previous_errno;
+ }
+
+ int fd_ = -1;
+
+ // Template magic to use Closer::Tag if available, and do nothing if not.
+ // If Closer::Tag exists, this implementation is preferred, because int is a better match.
+ // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
+ template <typename T = Closer>
+ static auto tag(int fd, void* old_tag, void* new_tag)
+ -> decltype(T::Tag(fd, old_tag, new_tag), void()) {
+ T::Tag(fd, old_tag, new_tag);
+ }
+
+ template <typename T = Closer>
+ static void tag(long, void*, void*) {
+ // No-op.
+ }
+
+ // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
+ template <typename T = Closer>
+ static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
+ T::Close(fd, tag_value);
+ }
+
+ template <typename T = Closer>
+ static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
+ T::Close(fd);
+ }
+};
+
+using unique_fd = unique_fd_impl<DefaultCloser>;
+
+#if !defined(_WIN32)
+
+// Inline functions, so that they can be used header-only.
+template <typename Closer>
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
+ int flags = O_CLOEXEC) {
+ int pipefd[2];
+
+#if defined(__linux__)
+ if (pipe2(pipefd, flags) != 0) {
+ return false;
+ }
+#else // defined(__APPLE__)
+ if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
+ return false;
+ }
+ if (pipe(pipefd) != 0) {
+ return false;
+ }
+
+ if (flags & O_CLOEXEC) {
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+ }
+ if (flags & O_NONBLOCK) {
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+
+template <typename Closer>
+inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
+ unique_fd_impl<Closer>* right) {
+ int sockfd[2];
+ if (socketpair(domain, type, protocol, sockfd) != 0) {
+ return false;
+ }
+ left->reset(sockfd[0]);
+ right->reset(sockfd[1]);
+ return true;
+}
+
+template <typename Closer>
+inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
+ return Socketpair(AF_UNIX, type, 0, left, right);
+}
+
+// Using fdopen with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
+ int fd = ufd.release();
+ FILE* file = fdopen(fd, mode);
+ if (!file) {
+ close(fd);
+ }
+ return file;
+}
+
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+ int fd = ufd.release();
+ DIR* dir = fdopendir(fd);
+ if (dir == nullptr) {
+ close(fd);
+ }
+ return dir;
+}
+
+#endif // !defined(_WIN32)
+
+// A wrapper type that can be implicitly constructed from either int or unique_fd.
+struct borrowed_fd {
+ /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
+ template <typename T>
+ /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {} // NOLINT
+
+ int get() const { return fd_; }
+
+ bool operator>=(int rhs) const { return get() >= rhs; }
+ bool operator<(int rhs) const { return get() < rhs; }
+ bool operator==(int rhs) const { return get() == rhs; }
+ bool operator!=(int rhs) const { return get() != rhs; }
+
+ private:
+ int fd_ = -1;
+};
+} // namespace base
+} // namespace android
+
+template <typename T>
+int close(const android::base::unique_fd_impl<T>&)
+ __attribute__((__unavailable__("close called on unique_fd")));
+
+template <typename T>
+FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
+ __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
+ "unique_fd, or use android::base::Fdopen to pass ownership")));
+
+template <typename T>
+DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
+ __unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
+ "unique_fd, or use android::base::Fdopendir to pass ownership")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
new file mode 100644
index 000000000..1a414ec79
--- /dev/null
+++ b/base/include/android-base/utf8.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef _WIN32
+#include <sys/types.h>
+#include <string>
+#else
+// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
+#include <fcntl.h> // open
+#include <stdio.h> // fopen
+#include <sys/stat.h> // mkdir
+#include <unistd.h> // unlink
+#endif
+
+namespace android {
+namespace base {
+
+// Only available on Windows because this is only needed on Windows.
+#ifdef _WIN32
+// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
+// conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
+
+// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
+// whether the conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
+
+// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
+// UTF-8. Returns whether the conversion was done successfully.
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
+
+// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
+// was done successfully.
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
+
+// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
+// whether the conversion was done successfully.
+bool UTF8ToWide(const char* utf8, std::wstring* utf16);
+
+// Convert a UTF-8 std::string (including any embedded NULL characters) to
+// UTF-16. Returns whether the conversion was done successfully.
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+
+// Convert a file system path, represented as a NULL-terminated string of
+// UTF-8 characters, to a UTF-16 string representing the same file system
+// path using the Windows extended-lengh path representation.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH:
+// ```The Windows API has many functions that also have Unicode versions to
+// permit an extended-length path for a maximum total path length of 32,767
+// characters. To specify an extended-length path, use the "\\?\" prefix.
+// For example, "\\?\D:\very long path".```
+//
+// Returns whether the conversion was done successfully.
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16);
+#endif
+
+// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
+// are wrappers, for non-Windows these just expose existing APIs. To call these
+// functions, use:
+//
+// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
+// namespace {
+// // Import functions into anonymous namespace.
+// using namespace android::base::utf8;
+//
+// void SomeFunction(const char* name) {
+// int fd = open(name, ...); // Calls android::base::utf8::open().
+// ...
+// unlink(name); // Calls android::base::utf8::unlink().
+// }
+// }
+namespace utf8 {
+
+#ifdef _WIN32
+FILE* fopen(const char* name, const char* mode);
+int mkdir(const char* name, mode_t mode);
+int open(const char* name, int flags, ...);
+int unlink(const char* name);
+#else
+using ::fopen;
+using ::mkdir;
+using ::open;
+using ::unlink;
+#endif
+
+} // namespace utf8
+} // namespace base
+} // namespace android
diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp
new file mode 100644
index 000000000..1f4b69b53
--- /dev/null
+++ b/base/liblog_symbols.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblog_symbols.h"
+
+#if defined(__ANDROID_SDK_VERSION__) && (__ANDROID_SDK_VERSION__ <= 29)
+#define USE_DLSYM
+#endif
+
+#ifdef USE_DLSYM
+#include <dlfcn.h>
+#endif
+
+namespace android {
+namespace base {
+
+#ifdef USE_DLSYM
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions() {
+ static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
+ void* liblog_handle = dlopen("liblog.so", RTLD_NOW);
+ if (liblog_handle == nullptr) {
+ return {};
+ }
+
+ LibLogFunctions real_liblog_functions = {};
+
+#define DLSYM(name) \
+ real_liblog_functions.name = \
+ reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \
+ if (real_liblog_functions.name == nullptr) { \
+ return {}; \
+ }
+
+ DLSYM(__android_log_set_logger)
+ DLSYM(__android_log_write_log_message)
+ DLSYM(__android_log_logd_logger)
+ DLSYM(__android_log_stderr_logger)
+ DLSYM(__android_log_set_aborter)
+ DLSYM(__android_log_call_aborter)
+ DLSYM(__android_log_default_aborter)
+ DLSYM(__android_log_set_minimum_priority);
+ DLSYM(__android_log_get_minimum_priority);
+ DLSYM(__android_log_set_default_tag);
+#undef DLSYM
+
+ return real_liblog_functions;
+ }();
+
+ return liblog_functions;
+}
+
+#else
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions() {
+ static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> {
+ return LibLogFunctions{
+ .__android_log_set_logger = __android_log_set_logger,
+ .__android_log_write_log_message = __android_log_write_log_message,
+ .__android_log_logd_logger = __android_log_logd_logger,
+ .__android_log_stderr_logger = __android_log_stderr_logger,
+ .__android_log_set_aborter = __android_log_set_aborter,
+ .__android_log_call_aborter = __android_log_call_aborter,
+ .__android_log_default_aborter = __android_log_default_aborter,
+ .__android_log_set_minimum_priority = __android_log_set_minimum_priority,
+ .__android_log_get_minimum_priority = __android_log_get_minimum_priority,
+ .__android_log_set_default_tag = __android_log_set_default_tag,
+ };
+ }();
+ return liblog_functions;
+}
+
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h
new file mode 100644
index 000000000..2e6b47f7d
--- /dev/null
+++ b/base/liblog_symbols.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android/log.h>
+
+namespace android {
+namespace base {
+
+struct LibLogFunctions {
+ void (*__android_log_set_logger)(__android_logger_function logger);
+ void (*__android_log_write_log_message)(struct __android_log_message* log_message);
+
+ void (*__android_log_logd_logger)(const struct __android_log_message* log_message);
+ void (*__android_log_stderr_logger)(const struct __android_log_message* log_message);
+
+ void (*__android_log_set_aborter)(__android_aborter_function aborter);
+ void (*__android_log_call_aborter)(const char* abort_message);
+ void (*__android_log_default_aborter)(const char* abort_message);
+ int32_t (*__android_log_set_minimum_priority)(int32_t priority);
+ int32_t (*__android_log_get_minimum_priority)();
+ void (*__android_log_set_default_tag)(const char* tag);
+};
+
+const std::optional<LibLogFunctions>& GetLibLogFunctions();
+
+} // namespace base
+} // namespace android
diff --git a/base/logging.cpp b/base/logging.cpp
new file mode 100644
index 000000000..5bd21da66
--- /dev/null
+++ b/base/logging.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include "android-base/logging.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <time.h>
+
+// For getprogname(3) or program_invocation_short_name.
+#if defined(__ANDROID__) || defined(__APPLE__)
+#include <stdlib.h>
+#elif defined(__GLIBC__)
+#include <errno.h>
+#endif
+
+#if defined(__linux__)
+#include <sys/uio.h>
+#endif
+
+#include <atomic>
+#include <iostream>
+#include <limits>
+#include <mutex>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android/log.h>
+#ifdef __ANDROID__
+#include <android/set_abort_message.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/threads.h>
+
+#include "liblog_symbols.h"
+#include "logging_splitters.h"
+
+namespace android {
+namespace base {
+
+// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
+#if defined(__GLIBC__)
+ return program_invocation_short_name;
+#elif defined(_WIN32)
+ static bool first = true;
+ static char progname[MAX_PATH] = {};
+
+ if (first) {
+ snprintf(progname, sizeof(progname), "%s",
+ android::base::Basename(android::base::GetExecutablePath()).c_str());
+ first = false;
+ }
+
+ return progname;
+#endif
+}
+#endif
+
+static const char* GetFileBasename(const char* file) {
+ // We can't use basename(3) even on Unix because the Mac doesn't
+ // have a non-modifying basename.
+ const char* last_slash = strrchr(file, '/');
+ if (last_slash != nullptr) {
+ return last_slash + 1;
+ }
+#if defined(_WIN32)
+ const char* last_backslash = strrchr(file, '\\');
+ if (last_backslash != nullptr) {
+ return last_backslash + 1;
+ }
+#endif
+ return file;
+}
+
+#if defined(__linux__)
+static int OpenKmsg() {
+#if defined(__ANDROID__)
+ // pick up 'file w /dev/kmsg' environment from daemon's init rc file
+ const auto val = getenv("ANDROID_FILE__dev_kmsg");
+ if (val != nullptr) {
+ int fd;
+ if (android::base::ParseInt(val, &fd, 0)) {
+ auto flags = fcntl(fd, F_GETFL);
+ if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
+ }
+ }
+#endif
+ return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
+}
+#endif
+
+static LogId log_id_tToLogId(int32_t buffer_id) {
+ switch (buffer_id) {
+ case LOG_ID_MAIN:
+ return MAIN;
+ case LOG_ID_SYSTEM:
+ return SYSTEM;
+ case LOG_ID_RADIO:
+ return RADIO;
+ case LOG_ID_CRASH:
+ return CRASH;
+ case LOG_ID_DEFAULT:
+ default:
+ return DEFAULT;
+ }
+}
+
+static int32_t LogIdTolog_id_t(LogId log_id) {
+ switch (log_id) {
+ case MAIN:
+ return LOG_ID_MAIN;
+ case SYSTEM:
+ return LOG_ID_SYSTEM;
+ case RADIO:
+ return LOG_ID_RADIO;
+ case CRASH:
+ return LOG_ID_CRASH;
+ case DEFAULT:
+ default:
+ return LOG_ID_DEFAULT;
+ }
+}
+
+static LogSeverity PriorityToLogSeverity(int priority) {
+ switch (priority) {
+ case ANDROID_LOG_DEFAULT:
+ return INFO;
+ case ANDROID_LOG_VERBOSE:
+ return VERBOSE;
+ case ANDROID_LOG_DEBUG:
+ return DEBUG;
+ case ANDROID_LOG_INFO:
+ return INFO;
+ case ANDROID_LOG_WARN:
+ return WARNING;
+ case ANDROID_LOG_ERROR:
+ return ERROR;
+ case ANDROID_LOG_FATAL:
+ return FATAL;
+ default:
+ return FATAL;
+ }
+}
+
+static int32_t LogSeverityToPriority(LogSeverity severity) {
+ switch (severity) {
+ case VERBOSE:
+ return ANDROID_LOG_VERBOSE;
+ case DEBUG:
+ return ANDROID_LOG_DEBUG;
+ case INFO:
+ return ANDROID_LOG_INFO;
+ case WARNING:
+ return ANDROID_LOG_WARN;
+ case ERROR:
+ return ANDROID_LOG_ERROR;
+ case FATAL_WITHOUT_ABORT:
+ case FATAL:
+ default:
+ return ANDROID_LOG_FATAL;
+ }
+}
+
+static LogFunction& Logger() {
+#ifdef __ANDROID__
+ static auto& logger = *new LogFunction(LogdLogger());
+#else
+ static auto& logger = *new LogFunction(StderrLogger);
+#endif
+ return logger;
+}
+
+static AbortFunction& Aborter() {
+ static auto& aborter = *new AbortFunction(DefaultAborter);
+ return aborter;
+}
+
+// Only used for Q fallback.
+static std::recursive_mutex& TagLock() {
+ static auto& tag_lock = *new std::recursive_mutex();
+ return tag_lock;
+}
+// Only used for Q fallback.
+static std::string* gDefaultTag;
+
+void SetDefaultTag(const std::string& tag) {
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ liblog_functions->__android_log_set_default_tag(tag.c_str());
+ } else {
+ std::lock_guard<std::recursive_mutex> lock(TagLock());
+ if (gDefaultTag != nullptr) {
+ delete gDefaultTag;
+ gDefaultTag = nullptr;
+ }
+ if (!tag.empty()) {
+ gDefaultTag = new std::string(tag);
+ }
+ }
+}
+
+static bool gInitialized = false;
+
+// Only used for Q fallback.
+static LogSeverity gMinimumLogSeverity = INFO;
+
+#if defined(__linux__)
+static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
+ const char* tag) {
+ // clang-format off
+ static constexpr int kLogSeverityToKernelLogLevel[] = {
+ [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
+ // level)
+ [android::base::DEBUG] = 7, // KERN_DEBUG
+ [android::base::INFO] = 6, // KERN_INFO
+ [android::base::WARNING] = 4, // KERN_WARNING
+ [android::base::ERROR] = 3, // KERN_ERROR
+ [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
+ [android::base::FATAL] = 2, // KERN_CRIT
+ };
+ // clang-format on
+ static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
+ "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
+
+ static int klog_fd = OpenKmsg();
+ if (klog_fd == -1) return;
+
+ int level = kLogSeverityToKernelLogLevel[severity];
+
+ // The kernel's printk buffer is only 1024 bytes.
+ // TODO: should we automatically break up long lines into multiple lines?
+ // Or we could log but with something like "..." at the end?
+ char buf[1024] __attribute__((__uninitialized__));
+ size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
+ if (size > sizeof(buf)) {
+ size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
+ level, tag, size);
+ }
+
+ iovec iov[1];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = size;
+ TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
+}
+
+void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+ const char*, unsigned int, const char* full_message) {
+ SplitByLines(full_message, KernelLogLine, severity, tag);
+}
+#endif
+
+void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ struct tm now;
+ time_t t = time(nullptr);
+
+#if defined(_WIN32)
+ localtime_s(&now, &t);
+#else
+ localtime_r(&t, &now);
+#endif
+ auto output_string =
+ StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
+
+ fputs(output_string.c_str(), stderr);
+}
+
+void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
+ unsigned int /*line*/, const char* message) {
+ if (severity >= WARNING) {
+ fflush(stdout);
+ fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+void DefaultAborter(const char* abort_message) {
+#ifdef __ANDROID__
+ android_set_abort_message(abort_message);
+#else
+ UNUSED(abort_message);
+#endif
+ abort();
+}
+
+static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
+ int32_t lg_id = LogIdTolog_id_t(id);
+ int32_t priority = LogSeverityToPriority(severity);
+
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ __android_log_message log_message = {sizeof(__android_log_message), lg_id, priority, tag,
+ static_cast<const char*>(nullptr), 0, message};
+ liblog_functions->__android_log_logd_logger(&log_message);
+ } else {
+ __android_log_buf_print(lg_id, priority, tag, "%s", message);
+ }
+}
+
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
+}
+
+void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
+ SetLogger(std::forward<LogFunction>(logger));
+ SetAborter(std::forward<AbortFunction>(aborter));
+
+ if (gInitialized) {
+ return;
+ }
+
+ gInitialized = true;
+
+ // Stash the command line for later use. We can use /proc/self/cmdline on
+ // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+ // and there are a couple of argv[0] variants that are commonly used.
+ if (argv != nullptr) {
+ SetDefaultTag(basename(argv[0]));
+ }
+
+ const char* tags = getenv("ANDROID_LOG_TAGS");
+ if (tags == nullptr) {
+ return;
+ }
+
+ std::vector<std::string> specs = Split(tags, " ");
+ for (size_t i = 0; i < specs.size(); ++i) {
+ // "tag-pattern:[vdiwefs]"
+ std::string spec(specs[i]);
+ if (spec.size() == 3 && StartsWith(spec, "*:")) {
+ switch (spec[2]) {
+ case 'v':
+ SetMinimumLogSeverity(VERBOSE);
+ continue;
+ case 'd':
+ SetMinimumLogSeverity(DEBUG);
+ continue;
+ case 'i':
+ SetMinimumLogSeverity(INFO);
+ continue;
+ case 'w':
+ SetMinimumLogSeverity(WARNING);
+ continue;
+ case 'e':
+ SetMinimumLogSeverity(ERROR);
+ continue;
+ case 'f':
+ SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
+ continue;
+ // liblog will even suppress FATAL if you say 's' for silent, but that's
+ // crazy!
+ case 's':
+ SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
+ continue;
+ }
+ }
+ LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
+ << ")";
+ }
+}
+
+void SetLogger(LogFunction&& logger) {
+ Logger() = std::move(logger);
+
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
+ auto log_id = log_id_tToLogId(log_message->buffer_id);
+ auto severity = PriorityToLogSeverity(log_message->priority);
+
+ Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
+ log_message->message);
+ });
+ }
+}
+
+void SetAborter(AbortFunction&& aborter) {
+ Aborter() = std::move(aborter);
+
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ liblog_functions->__android_log_set_aborter(
+ [](const char* abort_message) { Aborter()(abort_message); });
+ }
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+ public:
+ LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
+ : file_(GetFileBasename(file)),
+ line_number_(line),
+ severity_(severity),
+ tag_(tag),
+ error_(error) {}
+
+ const char* GetFile() const {
+ return file_;
+ }
+
+ unsigned int GetLineNumber() const {
+ return line_number_;
+ }
+
+ LogSeverity GetSeverity() const {
+ return severity_;
+ }
+
+ const char* GetTag() const { return tag_; }
+
+ int GetError() const {
+ return error_;
+ }
+
+ std::ostream& GetBuffer() {
+ return buffer_;
+ }
+
+ std::string ToString() const {
+ return buffer_.str();
+ }
+
+ private:
+ std::ostringstream buffer_;
+ const char* const file_;
+ const unsigned int line_number_;
+ const LogSeverity severity_;
+ const char* const tag_;
+ const int error_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
+ const char* tag, int error)
+ : LogMessage(file, line, severity, tag, error) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
+ : data_(new LogMessageData(file, line, severity, tag, error)) {}
+
+LogMessage::~LogMessage() {
+ // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
+ if (!WOULD_LOG(data_->GetSeverity())) {
+ return;
+ }
+
+ // Finish constructing the message.
+ if (data_->GetError() != -1) {
+ data_->GetBuffer() << ": " << strerror(data_->GetError());
+ }
+ std::string msg(data_->ToString());
+
+ if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+ // Set the bionic abort message early to avoid liblog doing it
+ // with the individual lines, so that we get the whole message.
+ android_set_abort_message(msg.c_str());
+#endif
+ }
+
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+ msg.c_str());
+
+ // Abort if necessary.
+ if (data_->GetSeverity() == FATAL) {
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ liblog_functions->__android_log_call_aborter(msg.c_str());
+ } else {
+ Aborter()(msg.c_str());
+ }
+ }
+}
+
+std::ostream& LogMessage::stream() {
+ return data_->GetBuffer();
+}
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ const char* message) {
+ static auto& liblog_functions = GetLibLogFunctions();
+ int32_t priority = LogSeverityToPriority(severity);
+ if (liblog_functions) {
+ __android_log_message log_message = {
+ sizeof(__android_log_message), LOG_ID_DEFAULT, priority, tag, file, line, message};
+ liblog_functions->__android_log_write_log_message(&log_message);
+ } else {
+ if (tag == nullptr) {
+ std::lock_guard<std::recursive_mutex> lock(TagLock());
+ if (gDefaultTag == nullptr) {
+ gDefaultTag = new std::string(getprogname());
+ }
+
+ Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
+ } else {
+ Logger()(DEFAULT, severity, tag, file, line, message);
+ }
+ }
+}
+
+LogSeverity GetMinimumLogSeverity() {
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
+ } else {
+ return gMinimumLogSeverity;
+ }
+}
+
+bool ShouldLog(LogSeverity severity, const char* tag) {
+ static auto& liblog_functions = GetLibLogFunctions();
+ // Even though we're not using the R liblog functions in this function, if we're running on Q,
+ // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
+ // take into consideration the value from SetMinimumLogSeverity().
+ if (liblog_functions) {
+ int32_t priority = LogSeverityToPriority(severity);
+ return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
+ } else {
+ return severity >= gMinimumLogSeverity;
+ }
+}
+
+LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
+ static auto& liblog_functions = GetLibLogFunctions();
+ if (liblog_functions) {
+ int32_t priority = LogSeverityToPriority(new_severity);
+ return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
+ } else {
+ LogSeverity old_severity = gMinimumLogSeverity;
+ gMinimumLogSeverity = new_severity;
+ return old_severity;
+ }
+}
+
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
+ old_ = SetMinimumLogSeverity(new_severity);
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+ SetMinimumLogSeverity(old_);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_splitters.h b/base/logging_splitters.h
new file mode 100644
index 000000000..2ec2b205d
--- /dev/null
+++ b/base/logging_splitters.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
+
+namespace android {
+namespace base {
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character. It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ log_function(msg, newline - msg, args...);
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ log_function(msg, -1, args...);
+}
+
+// This splits the message up into chunks that logs can process delimited by new lines. It calls
+// log_function with the exact null terminated message that should be sent to logd.
+// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
+// this function simply calls log_function with msg without any extra overhead.
+template <typename F>
+static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* msg, const F& log_function) {
+ // The maximum size of a payload, after the log header that logd will accept is
+ // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
+ // the string that we can log in each pass.
+ // The protocol is documented in liblog/README.protocol.md.
+ // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
+ // and an additional byte for the null terminator on the payload. We subtract an additional 32
+ // bytes for slack, similar to java/android/util/Log.java.
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ if (max_size <= 0) {
+ abort();
+ }
+ // If we're logging a fatal message, we'll append the file and line numbers.
+ bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
+
+ std::string file_header;
+ if (add_file) {
+ file_header = StringPrintf("%s:%u] ", file, line);
+ }
+ int file_header_size = file_header.size();
+
+ __attribute__((uninitialized)) char logd_chunk[max_size + 1];
+ ptrdiff_t chunk_position = 0;
+
+ auto call_log_function = [&]() {
+ log_function(log_id, severity, tag, logd_chunk);
+ chunk_position = 0;
+ };
+
+ auto write_to_logd_chunk = [&](const char* message, int length) {
+ int size_written = 0;
+ const char* new_line = chunk_position > 0 ? "\n" : "";
+ if (add_file) {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%s%.*s", new_line, file_header.c_str(), length, message);
+ } else {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%.*s", new_line, length, message);
+ }
+
+ // This should never fail, if it does and we set size_written to 0, which will skip this line
+ // and move to the next one.
+ if (size_written < 0) {
+ size_written = 0;
+ }
+ chunk_position += size_written;
+ };
+
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ // If we have data in the buffer and this next line doesn't fit, write the buffer.
+ if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
+ call_log_function();
+ }
+
+ // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
+ // ever fit, in both cases, we add it to the buffer and continue.
+ write_to_logd_chunk(msg, newline - msg);
+
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
+ // then write the buffer.
+ if (chunk_position != 0 &&
+ chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
+ write_to_logd_chunk(msg, -1);
+ call_log_function();
+ } else {
+ // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
+ if (chunk_position != 0) {
+ call_log_function();
+ }
+ // Then write the rest of the msg.
+ if (add_file) {
+ snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
+ log_function(log_id, severity, tag, logd_chunk);
+ } else {
+ log_function(log_id, severity, tag, msg);
+ }
+ }
+}
+
+static std::pair<int, int> CountSizeAndNewLines(const char* message) {
+ int size = 0;
+ int new_lines = 0;
+ while (*message != '\0') {
+ size++;
+ if (*message == '\n') {
+ ++new_lines;
+ }
+ ++message;
+ }
+ return {size, new_lines};
+}
+
+// This adds the log header to each line of message and returns it as a string intended to be
+// written to stderr.
+static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
+ LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+ static const char log_characters[] = "VDIWEFF";
+ static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+ "Mismatch in size of log_characters and values in LogSeverity");
+ char severity_char = log_characters[severity];
+ std::string line_prefix;
+ if (file != nullptr) {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
+ severity_char, timestamp, pid, tid, file, line);
+ } else {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
+ timestamp, pid, tid);
+ }
+
+ auto [size, new_lines] = CountSizeAndNewLines(message);
+ std::string output_string;
+ output_string.reserve(size + new_lines * line_prefix.size() + 1);
+
+ auto concat_lines = [&](const char* message, int size) {
+ output_string.append(line_prefix);
+ if (size == -1) {
+ output_string.append(message);
+ } else {
+ output_string.append(message, size);
+ }
+ output_string.append("\n");
+ };
+ SplitByLines(message, concat_lines);
+ return output_string;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp
new file mode 100644
index 000000000..679d19ed1
--- /dev/null
+++ b/base/logging_splitters_test.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging_splitters.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+void TestNewlineSplitter(const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](const char* msg, int length) {
+ if (length == -1) {
+ output.push_back(msg);
+ } else {
+ output.push_back(std::string(msg, length));
+ }
+ };
+ SplitByLines(input.c_str(), logger_function);
+
+ EXPECT_EQ(expected_output, output);
+}
+
+TEST(logging_splitters, NewlineSplitter_EmptyString) {
+ TestNewlineSplitter("", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, NewlineSplitter_BasicString) {
+ TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
+ TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
+ TestNewlineSplitter("normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string", "second string", "thirdstring"});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
+ TestNewlineSplitter(
+ "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
+ TestNewlineSplitter(
+ "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
+}
+
+void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
+ const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
+ output.push_back(msg);
+ };
+
+ SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
+ input.c_str(), logger_function);
+
+ auto return_lengths = [&] {
+ std::string sizes;
+ sizes += "expected_output sizes:";
+ for (const auto& string : expected_output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ sizes += "\noutput sizes:";
+ for (const auto& string : output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ return sizes;
+ };
+
+ EXPECT_EQ(expected_output, output) << return_lengths();
+}
+
+TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
+ TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_BasicString) {
+ TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
+ TestLogdChunkSplitter(
+ "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
+}
+
+// This test should return the same string, the logd logger itself will truncate down to size.
+// This has historically been the behavior both in libbase and liblog.
+TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
+ auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
+
+ TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
+}
+
+std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
+ return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
+ auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
+ auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
+
+ auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
+
+ std::string tag = "tag";
+ std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
+ long_string_z};
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+// With a ~4k buffer, we should print 2 long strings per logger call.
+TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
+ std::vector expected = {
+ std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
+ std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
+ std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
+ };
+
+ auto long_strings = Join(expected, '\n');
+
+ TestLogdChunkSplitter("tag", "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
+ const char* tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ auto long_string_a = std::string(max_size, 'a');
+ auto long_string_b = std::string(max_size, 'b');
+ auto long_string_c = std::string(max_size, 'c');
+
+ auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
+
+ TestLogdChunkSplitter(tag, "", long_strings,
+ std::vector{long_string_a, long_string_b, long_string_c});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
+ std::string tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1;
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size, 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!\n\nfinal \n \n final \n");
+
+ std::vector expected = {first_string + '\n' + second_string, exact_string,
+ ReduceToMaxSize(tag, large_string), final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_WithFile) {
+ std::string tag = "tag";
+ std::string file = "/path/to/myfile.cpp";
+ int line = 1000;
+ auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size - file_header.size(), 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!");
+
+ std::vector expected = {
+ file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
+ file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, file, long_strings, expected);
+}
+
+// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
+// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
+// large, so we abort instead.
+TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
+ auto long_tag = std::string(5000, 'x');
+ auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
+ ASSERT_DEATH(
+ SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
+}
+
+// We do handle excessively large file names correctly however.
+TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
+ auto long_file = std::string(5000, 'x');
+ std::string tag = "tag";
+
+ std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
+
+ TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
+}
+
+void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
+ const std::string& expected) {
+ // All log messages will show "01-01 00:00:00"
+ struct tm now = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 1,
+ .tm_mon = 0,
+ .tm_year = 1970,
+ };
+
+ int pid = 1234; // All log messages will have 1234 for their PID.
+ uint64_t tid = 4321; // All log messages will have 4321 for their TID.
+
+ auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_Basic) {
+ TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
+ "nullptr E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
+ TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
+ "tag\n\n E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file\n\n", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
+ TestStderrOutputGenerator(
+ "tag", nullptr, 0, "simple message\n",
+ "tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
+ const char* expected_result =
+ "tag E 01-01 00:00:00 1234 4321 simple message\n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 another message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 final message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n";
+
+ TestStderrOutputGenerator("tag", nullptr, 0,
+ "simple message\n\n\nanother message \n\n final message \n\n\n",
+ expected_result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
+ auto long_string_a = std::string(4000, 'a');
+ auto long_string_b = std::string(4000, 'b');
+
+ auto message = long_string_a + '\n' + long_string_b;
+ auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' +
+ "tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n';
+ TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
new file mode 100644
index 000000000..593e2c100
--- /dev/null
+++ b/base/logging_test.cpp
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/logging.h"
+
+#include <libgen.h>
+
+#if defined(_WIN32)
+#include <signal.h>
+#endif
+
+#include <regex>
+#include <string>
+#include <thread>
+
+#include "android-base/file.h"
+#include "android-base/scopeguard.h"
+#include "android-base/stringprintf.h"
+#include "android-base/test_utils.h"
+
+#include <gtest/gtest.h>
+
+#ifdef __ANDROID__
+#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name)
+#else
+#define HOST_TEST(suite, name) TEST(suite, name)
+#endif
+
+#if defined(_WIN32)
+static void ExitSignalAbortHandler(int) {
+ _exit(3);
+}
+#endif
+
+static void SuppressAbortUI() {
+#if defined(_WIN32)
+ // We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
+ // suppress the Windows Error Reporting dialog box, but that API is not
+ // available in the OS-supplied C Runtime, msvcrt.dll, that we currently
+ // use (it is available in the Visual Studio C runtime).
+ //
+ // Instead, we setup a SIGABRT handler, which is called in abort() right
+ // before calling Windows Error Reporting. In the handler, we exit the
+ // process just like abort() does.
+ ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
+#endif
+}
+
+TEST(logging, CHECK) {
+ ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
+ CHECK(true);
+
+ ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
+ CHECK_EQ(0, 0);
+
+ ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
+ R"(Check failed: "foo" == "bar")");
+ CHECK_STREQ("foo", "foo");
+
+ // Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
+ bool flag = false;
+ if (true)
+ CHECK(true);
+ else
+ flag = true;
+ EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
+
+ flag = false;
+ if (true)
+ CHECK_STREQ("foo", "foo");
+ else
+ flag = true;
+ EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
+}
+
+TEST(logging, DCHECK) {
+ if (android::base::kEnableDChecks) {
+ ASSERT_DEATH({SuppressAbortUI(); DCHECK(false);}, "DCheck failed: false ");
+ }
+ DCHECK(true);
+
+ if (android::base::kEnableDChecks) {
+ ASSERT_DEATH({SuppressAbortUI(); DCHECK_EQ(0, 1);}, "DCheck failed: 0 == 1 ");
+ }
+ DCHECK_EQ(0, 0);
+
+ if (android::base::kEnableDChecks) {
+ ASSERT_DEATH({SuppressAbortUI(); DCHECK_STREQ("foo", "bar");},
+ R"(DCheck failed: "foo" == "bar")");
+ }
+ DCHECK_STREQ("foo", "foo");
+
+ // No testing whether we have a dangling else, possibly. That's inherent to the if (constexpr)
+ // setup we intentionally chose to force type-checks of debug code even in release builds (so
+ // we don't get more bit-rot).
+}
+
+
+#define CHECK_WOULD_LOG_DISABLED(severity) \
+ static_assert(android::base::severity < android::base::FATAL, "Bad input"); \
+ for (size_t i = static_cast<size_t>(android::base::severity) + 1; \
+ i <= static_cast<size_t>(android::base::FATAL); \
+ ++i) { \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_FALSE(WOULD_LOG(severity)) << i; \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_FALSE(WOULD_LOG(::android::base::severity)) << i; \
+ } \
+ } \
+
+#define CHECK_WOULD_LOG_ENABLED(severity) \
+ for (size_t i = static_cast<size_t>(android::base::VERBOSE); \
+ i <= static_cast<size_t>(android::base::severity); \
+ ++i) { \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_TRUE(WOULD_LOG(severity)) << i; \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(static_cast<android::base::LogSeverity>(i)); \
+ EXPECT_TRUE(WOULD_LOG(::android::base::severity)) << i; \
+ } \
+ } \
+
+TEST(logging, WOULD_LOG_FATAL) {
+ CHECK_WOULD_LOG_ENABLED(FATAL);
+}
+
+TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) {
+ CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT);
+}
+
+TEST(logging, WOULD_LOG_ERROR_disabled) {
+ CHECK_WOULD_LOG_DISABLED(ERROR);
+}
+
+TEST(logging, WOULD_LOG_ERROR_enabled) {
+ CHECK_WOULD_LOG_ENABLED(ERROR);
+}
+
+TEST(logging, WOULD_LOG_WARNING_disabled) {
+ CHECK_WOULD_LOG_DISABLED(WARNING);
+}
+
+TEST(logging, WOULD_LOG_WARNING_enabled) {
+ CHECK_WOULD_LOG_ENABLED(WARNING);
+}
+
+TEST(logging, WOULD_LOG_INFO_disabled) {
+ CHECK_WOULD_LOG_DISABLED(INFO);
+}
+
+TEST(logging, WOULD_LOG_INFO_enabled) {
+ CHECK_WOULD_LOG_ENABLED(INFO);
+}
+
+TEST(logging, WOULD_LOG_DEBUG_disabled) {
+ CHECK_WOULD_LOG_DISABLED(DEBUG);
+}
+
+TEST(logging, WOULD_LOG_DEBUG_enabled) {
+ CHECK_WOULD_LOG_ENABLED(DEBUG);
+}
+
+TEST(logging, WOULD_LOG_VERBOSE_disabled) {
+ CHECK_WOULD_LOG_DISABLED(VERBOSE);
+}
+
+TEST(logging, WOULD_LOG_VERBOSE_enabled) {
+ CHECK_WOULD_LOG_ENABLED(VERBOSE);
+}
+
+#undef CHECK_WOULD_LOG_DISABLED
+#undef CHECK_WOULD_LOG_ENABLED
+
+
+#if !defined(_WIN32)
+static std::string make_log_pattern(android::base::LogSeverity severity,
+ const char* message) {
+ static const char log_characters[] = "VDIWEFF";
+ static_assert(arraysize(log_characters) - 1 == android::base::FATAL + 1,
+ "Mismatch in size of log_characters and values in LogSeverity");
+ char log_char = log_characters[severity];
+ std::string holder(__FILE__);
+ return android::base::StringPrintf(
+ "%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
+ log_char, basename(&holder[0]), message);
+}
+#endif
+
+static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
+ const char* expected, const char* expected_tag = nullptr) {
+ // We can't usefully check the output of any of these on Windows because we
+ // don't have std::regex, but we can at least make sure we printed at least as
+ // many characters are in the log message.
+ ASSERT_GT(output.length(), strlen(expected));
+ ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
+ if (expected_tag != nullptr) {
+ ASSERT_NE(nullptr, strstr(output.c_str(), expected_tag)) << output;
+ }
+
+#if !defined(_WIN32)
+ std::string regex_str;
+ if (expected_tag != nullptr) {
+ regex_str.append(expected_tag);
+ regex_str.append(" ");
+ }
+ regex_str.append(make_log_pattern(severity, expected));
+ std::regex message_regex(regex_str);
+ ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
+}
+
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+ const char* expected, const char* expected_tag = nullptr) {
+ cap.Stop();
+ std::string output = cap.str();
+ return CheckMessage(output, severity, expected, expected_tag);
+}
+
+#define CHECK_LOG_STREAM_DISABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG_STREAM(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG_STREAM(::android::base::severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ }
+
+#define CHECK_LOG_STREAM_ENABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG_STREAM(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG_STREAM(::android::base::severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+
+TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
+}
+
+TEST(logging, LOG_STREAM_ERROR_disabled) {
+ CHECK_LOG_STREAM_DISABLED(ERROR);
+}
+
+TEST(logging, LOG_STREAM_ERROR_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
+}
+
+TEST(logging, LOG_STREAM_WARNING_disabled) {
+ CHECK_LOG_STREAM_DISABLED(WARNING);
+}
+
+TEST(logging, LOG_STREAM_WARNING_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
+}
+
+TEST(logging, LOG_STREAM_INFO_disabled) {
+ CHECK_LOG_STREAM_DISABLED(INFO);
+}
+
+TEST(logging, LOG_STREAM_INFO_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
+}
+
+TEST(logging, LOG_STREAM_DEBUG_disabled) {
+ CHECK_LOG_STREAM_DISABLED(DEBUG);
+}
+
+TEST(logging, LOG_STREAM_DEBUG_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
+}
+
+TEST(logging, LOG_STREAM_VERBOSE_disabled) {
+ CHECK_LOG_STREAM_DISABLED(VERBOSE);
+}
+
+TEST(logging, LOG_STREAM_VERBOSE_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
+}
+
+#undef CHECK_LOG_STREAM_DISABLED
+#undef CHECK_LOG_STREAM_ENABLED
+
+#define CHECK_LOG_DISABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ LOG(::android::base::severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ }
+
+#define CHECK_LOG_ENABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ LOG(::android::base::severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar"); \
+ } \
+
+TEST(logging, LOG_FATAL) {
+ ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
+ ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar");
+}
+
+TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
+}
+
+TEST(logging, LOG_ERROR_disabled) {
+ CHECK_LOG_DISABLED(ERROR);
+}
+
+TEST(logging, LOG_ERROR_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
+}
+
+TEST(logging, LOG_WARNING_disabled) {
+ CHECK_LOG_DISABLED(WARNING);
+}
+
+TEST(logging, LOG_WARNING_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
+}
+
+TEST(logging, LOG_INFO_disabled) {
+ CHECK_LOG_DISABLED(INFO);
+}
+
+TEST(logging, LOG_INFO_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
+}
+
+TEST(logging, LOG_DEBUG_disabled) {
+ CHECK_LOG_DISABLED(DEBUG);
+}
+
+TEST(logging, LOG_DEBUG_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
+}
+
+TEST(logging, LOG_VERBOSE_disabled) {
+ CHECK_LOG_DISABLED(VERBOSE);
+}
+
+TEST(logging, LOG_VERBOSE_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
+}
+
+#undef CHECK_LOG_DISABLED
+#undef CHECK_LOG_ENABLED
+
+TEST(logging, LOG_complex_param) {
+#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
+ { \
+ android::base::ScopedLogSeverity sls( \
+ (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
+ CapturedStderr cap; \
+ LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
+ << "foobar"; \
+ if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
+ ASSERT_NO_FATAL_FAILURE(CheckMessage( \
+ cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
+ "foobar")); \
+ } else { \
+ cap.Stop(); \
+ ASSERT_EQ("", cap.str()); \
+ } \
+ }
+
+ CHECK_LOG_COMBINATION(false,false);
+ CHECK_LOG_COMBINATION(false,true);
+ CHECK_LOG_COMBINATION(true,false);
+ CHECK_LOG_COMBINATION(true,true);
+
+#undef CHECK_LOG_COMBINATION
+}
+
+
+TEST(logging, LOG_does_not_clobber_errno) {
+ CapturedStderr cap;
+ errno = 12345;
+ LOG(INFO) << (errno = 67890);
+ EXPECT_EQ(12345, errno) << "errno was not restored";
+
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
+}
+
+TEST(logging, PLOG_does_not_clobber_errno) {
+ CapturedStderr cap;
+ errno = 12345;
+ PLOG(INFO) << (errno = 67890);
+ EXPECT_EQ(12345, errno) << "errno was not restored";
+
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
+}
+
+TEST(logging, LOG_does_not_have_dangling_if) {
+ CapturedStderr cap; // So the logging below has no side-effects.
+
+ // Do the test two ways: once where we hypothesize that LOG()'s if
+ // will evaluate to true (when severity is high enough) and once when we
+ // expect it to evaluate to false (when severity is not high enough).
+ bool flag = false;
+ if (true)
+ LOG(INFO) << "foobar";
+ else
+ flag = true;
+
+ EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
+
+ flag = false;
+ if (true)
+ LOG(VERBOSE) << "foobar";
+ else
+ flag = true;
+
+ EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
+}
+
+#define CHECK_PLOG_DISABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ PLOG(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls1(android::base::FATAL); \
+ CapturedStderr cap1; \
+ PLOG(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ }
+
+#define CHECK_PLOG_ENABLED(severity) \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ errno = ENOENT; \
+ PLOG(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
+ } \
+ { \
+ android::base::ScopedLogSeverity sls2(android::base::severity); \
+ CapturedStderr cap2; \
+ errno = ENOENT; \
+ PLOG(severity) << "foobar"; \
+ CheckMessage(cap2, android::base::severity, "foobar: No such file or directory"); \
+ } \
+
+TEST(logging, PLOG_FATAL) {
+ ASSERT_DEATH({SuppressAbortUI(); PLOG(FATAL) << "foobar";}, "foobar");
+ ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar");
+}
+
+TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
+}
+
+TEST(logging, PLOG_ERROR_disabled) {
+ CHECK_PLOG_DISABLED(ERROR);
+}
+
+TEST(logging, PLOG_ERROR_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
+}
+
+TEST(logging, PLOG_WARNING_disabled) {
+ CHECK_PLOG_DISABLED(WARNING);
+}
+
+TEST(logging, PLOG_WARNING_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
+}
+
+TEST(logging, PLOG_INFO_disabled) {
+ CHECK_PLOG_DISABLED(INFO);
+}
+
+TEST(logging, PLOG_INFO_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
+}
+
+TEST(logging, PLOG_DEBUG_disabled) {
+ CHECK_PLOG_DISABLED(DEBUG);
+}
+
+TEST(logging, PLOG_DEBUG_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
+}
+
+TEST(logging, PLOG_VERBOSE_disabled) {
+ CHECK_PLOG_DISABLED(VERBOSE);
+}
+
+TEST(logging, PLOG_VERBOSE_enabled) {
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
+}
+
+#undef CHECK_PLOG_DISABLED
+#undef CHECK_PLOG_ENABLED
+
+
+TEST(logging, UNIMPLEMENTED) {
+ std::string expected = android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
+
+ CapturedStderr cap;
+ errno = ENOENT;
+ UNIMPLEMENTED(ERROR);
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
+}
+
+static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
+ LOG(ERROR) << "called noop";
+}
+
+TEST(logging, LOG_FATAL_NOOP_ABORTER) {
+ CapturedStderr cap;
+ {
+ android::base::SetAborter(NoopAborter);
+
+ android::base::ScopedLogSeverity sls(android::base::ERROR);
+ LOG(FATAL) << "foobar";
+ cap.Stop();
+
+ android::base::SetAborter(android::base::DefaultAborter);
+ }
+ std::string output = cap.str();
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
+
+ ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
+}
+
+struct CountLineAborter {
+ static void CountLineAborterFunction(const char* msg) {
+ while (*msg != 0) {
+ if (*msg == '\n') {
+ newline_count++;
+ }
+ msg++;
+ }
+ }
+ static size_t newline_count;
+};
+size_t CountLineAborter::newline_count = 0;
+
+TEST(logging, LOG_FATAL_ABORTER_MESSAGE) {
+ CountLineAborter::newline_count = 0;
+ android::base::SetAborter(CountLineAborter::CountLineAborterFunction);
+
+ android::base::ScopedLogSeverity sls(android::base::ERROR);
+ CapturedStderr cap;
+ LOG(FATAL) << "foo\nbar";
+
+ EXPECT_EQ(CountLineAborter::newline_count, 1U);
+}
+
+__attribute__((constructor)) void TestLoggingInConstructor() {
+ LOG(ERROR) << "foobar";
+}
+
+TEST(logging, StdioLogger) {
+ CapturedStderr cap_err;
+ CapturedStdout cap_out;
+ android::base::SetLogger(android::base::StdioLogger);
+ LOG(INFO) << "out";
+ LOG(ERROR) << "err";
+ cap_err.Stop();
+ cap_out.Stop();
+
+ // For INFO we expect just the literal "out\n".
+ ASSERT_EQ("out\n", cap_out.str());
+ // Whereas ERROR logging includes the program name.
+ ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
+}
+
+TEST(logging, ForkSafe) {
+#if !defined(_WIN32)
+ using namespace android::base;
+ SetLogger(
+ [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
+
+ auto guard = make_scope_guard([&] {
+#ifdef __ANDROID__
+ SetLogger(LogdLogger());
+#else
+ SetLogger(StderrLogger);
+#endif
+ });
+
+ auto thread = std::thread([] {
+ LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
+ "is no intervention";
+ });
+ thread.detach();
+
+ auto pid = fork();
+ ASSERT_NE(-1, pid);
+
+ if (pid == 0) {
+ // Reset the logger, so the next message doesn't sleep().
+ SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
+ LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
+ _exit(EXIT_SUCCESS);
+ }
+
+ // Wait for up to 3 seconds for the child to exit.
+ int tries = 3;
+ bool found_child = false;
+ while (tries-- > 0) {
+ auto result = waitpid(pid, nullptr, WNOHANG);
+ EXPECT_NE(-1, result);
+ if (result == pid) {
+ found_child = true;
+ break;
+ }
+ sleep(1);
+ }
+
+ EXPECT_TRUE(found_child);
+
+ // Kill the child if it did not exit.
+ if (!found_child) {
+ kill(pid, SIGKILL);
+ }
+#endif
+}
diff --git a/base/macros_test.cpp b/base/macros_test.cpp
new file mode 100644
index 000000000..2b522db5c
--- /dev/null
+++ b/base/macros_test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/macros.h"
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+TEST(macros, SIZEOF_MEMBER_macro) {
+ struct S {
+ int32_t i32;
+ double d;
+ };
+ ASSERT_EQ(4U, SIZEOF_MEMBER(S, i32));
+ ASSERT_EQ(8U, SIZEOF_MEMBER(S, d));
+}
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
new file mode 100644
index 000000000..fff345384
--- /dev/null
+++ b/base/mapped_file.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/mapped_file.h"
+
+#include <utility>
+
+#include <errno.h>
+
+namespace android {
+namespace base {
+
+static constexpr char kEmptyBuffer[] = {'0'};
+
+static off64_t InitPageSize() {
+#if defined(_WIN32)
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwAllocationGranularity;
+#else
+ return sysconf(_SC_PAGE_SIZE);
+#endif
+}
+
+std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
+ int prot) {
+#if defined(_WIN32)
+ return FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
+#else
+ return FromOsHandle(fd.get(), offset, length, prot);
+#endif
+}
+
+std::unique_ptr<MappedFile> MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length,
+ int prot) {
+ static const off64_t page_size = InitPageSize();
+ size_t slop = offset % page_size;
+ off64_t file_offset = offset - slop;
+ off64_t file_length = length + slop;
+
+#if defined(_WIN32)
+ HANDLE handle = CreateFileMappingW(
+ h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+ if (handle == nullptr) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // Return a MappedFile that's only valid for reading the size.
+ if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
+ return std::unique_ptr<MappedFile>(
+ new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0, nullptr));
+ }
+ return nullptr;
+ }
+ void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
+ file_offset, file_length);
+ if (base == nullptr) {
+ CloseHandle(handle);
+ return nullptr;
+ }
+ return std::unique_ptr<MappedFile>(
+ new MappedFile(static_cast<char*>(base), length, slop, handle));
+#else
+ void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
+ if (base == MAP_FAILED) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // mmap fails with EINVAL for a zero length region.
+ if (errno == EINVAL && length == 0) {
+ return std::unique_ptr<MappedFile>(new MappedFile(const_cast<char*>(kEmptyBuffer), 0, 0));
+ }
+ return nullptr;
+ }
+ return std::unique_ptr<MappedFile>(new MappedFile(static_cast<char*>(base), length, slop));
+#endif
+}
+
+MappedFile::MappedFile(MappedFile&& other)
+ : base_(std::exchange(other.base_, nullptr)),
+ size_(std::exchange(other.size_, 0)),
+ offset_(std::exchange(other.offset_, 0))
+#ifdef _WIN32
+ ,
+ handle_(std::exchange(other.handle_, nullptr))
+#endif
+{
+}
+
+MappedFile& MappedFile::operator=(MappedFile&& other) {
+ Close();
+ base_ = std::exchange(other.base_, nullptr);
+ size_ = std::exchange(other.size_, 0);
+ offset_ = std::exchange(other.offset_, 0);
+#ifdef _WIN32
+ handle_ = std::exchange(other.handle_, nullptr);
+#endif
+ return *this;
+}
+
+MappedFile::~MappedFile() {
+ Close();
+}
+
+void MappedFile::Close() {
+#if defined(_WIN32)
+ if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
+ if (handle_ != nullptr) CloseHandle(handle_);
+ handle_ = nullptr;
+#else
+ if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
+#endif
+
+ base_ = nullptr;
+ offset_ = size_ = 0;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
new file mode 100644
index 000000000..d21703c78
--- /dev/null
+++ b/base/mapped_file_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/mapped_file.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/file.h"
+
+TEST(mapped_file, smoke) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("hello world", tf.fd));
+
+ auto m = android::base::MappedFile::FromFd(tf.fd, 3, 2, PROT_READ);
+ ASSERT_EQ(2u, m->size());
+ ASSERT_EQ('l', m->data()[0]);
+ ASSERT_EQ('o', m->data()[1]);
+}
+
+TEST(mapped_file, zero_length_mapping) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // mmap fails with EINVAL for a zero length region.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
+ EXPECT_EQ(0u, m->size());
+ EXPECT_NE(nullptr, m->data());
+}
diff --git a/base/no_destructor_test.cpp b/base/no_destructor_test.cpp
new file mode 100644
index 000000000..f19468abf
--- /dev/null
+++ b/base/no_destructor_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/no_destructor.h"
+
+#include <gtest/gtest.h>
+
+struct __attribute__((packed)) Bomb {
+ Bomb() : magic_(123) {}
+
+ ~Bomb() { exit(42); }
+
+ int get() const { return magic_; }
+
+ private:
+ [[maybe_unused]] char padding_;
+ int magic_;
+};
+
+TEST(no_destructor, bomb) {
+ ASSERT_EXIT(({
+ {
+ Bomb b;
+ if (b.get() != 123) exit(1);
+ }
+
+ exit(0);
+ }),
+ ::testing::ExitedWithCode(42), "");
+}
+
+TEST(no_destructor, defused) {
+ ASSERT_EXIT(({
+ {
+ android::base::NoDestructor<Bomb> b;
+ if (b->get() != 123) exit(1);
+ }
+
+ exit(0);
+ }),
+ ::testing::ExitedWithCode(0), "");
+}
+
+TEST(no_destructor, operators) {
+ android::base::NoDestructor<Bomb> b;
+ const android::base::NoDestructor<Bomb>& c = b;
+ ASSERT_EQ(123, b.get()->get());
+ ASSERT_EQ(123, b->get());
+ ASSERT_EQ(123, (*b).get());
+ ASSERT_EQ(123, c.get()->get());
+ ASSERT_EQ(123, c->get());
+ ASSERT_EQ(123, (*c).get());
+}
diff --git a/base/parsebool.cpp b/base/parsebool.cpp
new file mode 100644
index 000000000..ff96fe9a9
--- /dev/null
+++ b/base/parsebool.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsebool.h"
+#include <errno.h>
+
+namespace android {
+namespace base {
+
+ParseBoolResult ParseBool(std::string_view s) {
+ if (s == "1" || s == "y" || s == "yes" || s == "on" || s == "true") {
+ return ParseBoolResult::kTrue;
+ }
+ if (s == "0" || s == "n" || s == "no" || s == "off" || s == "false") {
+ return ParseBoolResult::kFalse;
+ }
+ return ParseBoolResult::kError;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/parsebool_test.cpp b/base/parsebool_test.cpp
new file mode 100644
index 000000000..a0819940b
--- /dev/null
+++ b/base/parsebool_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsebool.h"
+
+#include <errno.h>
+
+#include <gtest/gtest.h>
+#include <string_view>
+
+using android::base::ParseBool;
+using android::base::ParseBoolResult;
+
+TEST(parsebool, true_) {
+ static const char* yes[] = {
+ "1", "on", "true", "y", "yes",
+ };
+ for (const char* s : yes) {
+ ASSERT_EQ(ParseBoolResult::kTrue, ParseBool(s));
+ }
+}
+
+TEST(parsebool, false_) {
+ static const char* no[] = {
+ "0", "false", "n", "no", "off",
+ };
+ for (const char* s : no) {
+ ASSERT_EQ(ParseBoolResult::kFalse, ParseBool(s));
+ }
+}
+
+TEST(parsebool, invalid) {
+ ASSERT_EQ(ParseBoolResult::kError, ParseBool("blarg"));
+ ASSERT_EQ(ParseBoolResult::kError, ParseBool(""));
+}
diff --git a/base/parsedouble_test.cpp b/base/parsedouble_test.cpp
new file mode 100644
index 000000000..ec3c10c74
--- /dev/null
+++ b/base/parsedouble_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsedouble.h"
+
+#include <gtest/gtest.h>
+
+TEST(parsedouble, double_smoke) {
+ double d;
+ ASSERT_FALSE(android::base::ParseDouble("", &d));
+ ASSERT_FALSE(android::base::ParseDouble("x", &d));
+ ASSERT_FALSE(android::base::ParseDouble("123.4x", &d));
+
+ ASSERT_TRUE(android::base::ParseDouble("123.4", &d));
+ ASSERT_DOUBLE_EQ(123.4, d);
+ ASSERT_TRUE(android::base::ParseDouble("-123.4", &d));
+ ASSERT_DOUBLE_EQ(-123.4, d);
+
+ ASSERT_TRUE(android::base::ParseDouble("0", &d, 0.0));
+ ASSERT_DOUBLE_EQ(0.0, d);
+ ASSERT_FALSE(android::base::ParseDouble("0", &d, 1e-9));
+ ASSERT_FALSE(android::base::ParseDouble("3.0", &d, -1.0, 2.0));
+ ASSERT_TRUE(android::base::ParseDouble("1.0", &d, 0.0, 2.0));
+ ASSERT_DOUBLE_EQ(1.0, d);
+
+ ASSERT_FALSE(android::base::ParseDouble("123.4x", nullptr));
+ ASSERT_TRUE(android::base::ParseDouble("-123.4", nullptr));
+ ASSERT_FALSE(android::base::ParseDouble("3.0", nullptr, -1.0, 2.0));
+ ASSERT_TRUE(android::base::ParseDouble("1.0", nullptr, 0.0, 2.0));
+}
+
+TEST(parsedouble, float_smoke) {
+ float f;
+ ASSERT_FALSE(android::base::ParseFloat("", &f));
+ ASSERT_FALSE(android::base::ParseFloat("x", &f));
+ ASSERT_FALSE(android::base::ParseFloat("123.4x", &f));
+
+ ASSERT_TRUE(android::base::ParseFloat("123.4", &f));
+ ASSERT_FLOAT_EQ(123.4, f);
+ ASSERT_TRUE(android::base::ParseFloat("-123.4", &f));
+ ASSERT_FLOAT_EQ(-123.4, f);
+
+ ASSERT_TRUE(android::base::ParseFloat("0", &f, 0.0));
+ ASSERT_FLOAT_EQ(0.0, f);
+ ASSERT_FALSE(android::base::ParseFloat("0", &f, 1e-9));
+ ASSERT_FALSE(android::base::ParseFloat("3.0", &f, -1.0, 2.0));
+ ASSERT_TRUE(android::base::ParseFloat("1.0", &f, 0.0, 2.0));
+ ASSERT_FLOAT_EQ(1.0, f);
+
+ ASSERT_FALSE(android::base::ParseFloat("123.4x", nullptr));
+ ASSERT_TRUE(android::base::ParseFloat("-123.4", nullptr));
+ ASSERT_FALSE(android::base::ParseFloat("3.0", nullptr, -1.0, 2.0));
+ ASSERT_TRUE(android::base::ParseFloat("1.0", nullptr, 0.0, 2.0));
+}
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
new file mode 100644
index 000000000..e449c3345
--- /dev/null
+++ b/base/parseint_test.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parseint.h"
+
+#include <errno.h>
+
+#include <gtest/gtest.h>
+
+TEST(parseint, signed_smoke) {
+ errno = 0;
+ int i = 0;
+ ASSERT_FALSE(android::base::ParseInt("x", &i));
+ ASSERT_EQ(EINVAL, errno);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseInt("123x", &i));
+ ASSERT_EQ(EINVAL, errno);
+
+ ASSERT_TRUE(android::base::ParseInt("123", &i));
+ ASSERT_EQ(123, i);
+ ASSERT_EQ(0, errno);
+ i = 0;
+ EXPECT_TRUE(android::base::ParseInt(" 123", &i));
+ EXPECT_EQ(123, i);
+ ASSERT_TRUE(android::base::ParseInt("-123", &i));
+ ASSERT_EQ(-123, i);
+ i = 0;
+ EXPECT_TRUE(android::base::ParseInt(" -123", &i));
+ EXPECT_EQ(-123, i);
+
+ short s = 0;
+ ASSERT_TRUE(android::base::ParseInt("1234", &s));
+ ASSERT_EQ(1234, s);
+
+ ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
+ ASSERT_EQ(12, i);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
+ ASSERT_EQ(ERANGE, errno);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+ ASSERT_EQ(ERANGE, errno);
+
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
+ ASSERT_EQ(EINVAL, errno);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
+ ASSERT_EQ(EINVAL, errno);
+ ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
+}
+
+TEST(parseint, unsigned_smoke) {
+ errno = 0;
+ unsigned int i = 0u;
+ ASSERT_FALSE(android::base::ParseUint("x", &i));
+ ASSERT_EQ(EINVAL, errno);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseUint("123x", &i));
+ ASSERT_EQ(EINVAL, errno);
+
+ ASSERT_TRUE(android::base::ParseUint("123", &i));
+ ASSERT_EQ(123u, i);
+ ASSERT_EQ(0, errno);
+ i = 0u;
+ EXPECT_TRUE(android::base::ParseUint(" 123", &i));
+ EXPECT_EQ(123u, i);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseUint("-123", &i));
+ EXPECT_EQ(EINVAL, errno);
+ errno = 0;
+ EXPECT_FALSE(android::base::ParseUint(" -123", &i));
+ EXPECT_EQ(EINVAL, errno);
+
+ unsigned short s = 0u;
+ ASSERT_TRUE(android::base::ParseUint("1234", &s));
+ ASSERT_EQ(1234u, s);
+
+ ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
+ ASSERT_EQ(12u, i);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
+ ASSERT_EQ(EINVAL, errno);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+ ASSERT_EQ(ERANGE, errno);
+
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
+ ASSERT_EQ(EINVAL, errno);
+ errno = 0;
+ ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
+ ASSERT_EQ(EINVAL, errno);
+ ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
+
+ errno = 0;
+ unsigned long long int lli;
+ EXPECT_FALSE(android::base::ParseUint("-123", &lli));
+ EXPECT_EQ(EINVAL, errno);
+ errno = 0;
+ EXPECT_FALSE(android::base::ParseUint(" -123", &lli));
+ EXPECT_EQ(EINVAL, errno);
+}
+
+TEST(parseint, no_implicit_octal) {
+ int i = 0;
+ ASSERT_TRUE(android::base::ParseInt("0123", &i));
+ ASSERT_EQ(123, i);
+
+ unsigned int u = 0u;
+ ASSERT_TRUE(android::base::ParseUint("0123", &u));
+ ASSERT_EQ(123u, u);
+}
+
+TEST(parseint, explicit_hex) {
+ int i = 0;
+ ASSERT_TRUE(android::base::ParseInt("0x123", &i));
+ ASSERT_EQ(0x123, i);
+ i = 0;
+ EXPECT_TRUE(android::base::ParseInt(" 0x123", &i));
+ EXPECT_EQ(0x123, i);
+
+ unsigned int u = 0u;
+ ASSERT_TRUE(android::base::ParseUint("0x123", &u));
+ ASSERT_EQ(0x123u, u);
+ u = 0u;
+ EXPECT_TRUE(android::base::ParseUint(" 0x123", &u));
+ EXPECT_EQ(0x123u, u);
+}
+
+TEST(parseint, string) {
+ int i = 0;
+ ASSERT_TRUE(android::base::ParseInt(std::string("123"), &i));
+ ASSERT_EQ(123, i);
+
+ unsigned int u = 0u;
+ ASSERT_TRUE(android::base::ParseUint(std::string("123"), &u));
+ ASSERT_EQ(123u, u);
+}
+
+TEST(parseint, untouched_on_failure) {
+ int i = 123;
+ ASSERT_FALSE(android::base::ParseInt("456x", &i));
+ ASSERT_EQ(123, i);
+
+ unsigned int u = 123u;
+ ASSERT_FALSE(android::base::ParseUint("456x", &u));
+ ASSERT_EQ(123u, u);
+}
+
+TEST(parseint, ParseByteCount) {
+ uint64_t i = 0;
+ ASSERT_TRUE(android::base::ParseByteCount("123b", &i));
+ ASSERT_EQ(123ULL, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("8k", &i));
+ ASSERT_EQ(8ULL * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("8M", &i));
+ ASSERT_EQ(8ULL * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("6g", &i));
+ ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("1T", &i));
+ ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("2p", &i));
+ ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("4e", &i));
+ ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i);
+}
+
+TEST(parseint, ParseByteCount_invalid_suffix) {
+ unsigned u;
+ ASSERT_FALSE(android::base::ParseByteCount("1x", &u));
+}
+
+TEST(parseint, ParseByteCount_overflow) {
+ uint64_t u64;
+ ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64));
+
+ uint16_t u16;
+ ASSERT_TRUE(android::base::ParseByteCount("63k", &u16));
+ ASSERT_EQ(63U * 1024, u16);
+ ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16));
+ ASSERT_EQ(65535U, u16);
+ ASSERT_FALSE(android::base::ParseByteCount("65k", &u16));
+}
diff --git a/base/parsenetaddress.cpp b/base/parsenetaddress.cpp
new file mode 100644
index 000000000..dd80f6da2
--- /dev/null
+++ b/base/parsenetaddress.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsenetaddress.h"
+
+#include <algorithm>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+namespace android {
+namespace base {
+
+bool ParseNetAddress(const std::string& address, std::string* host, int* port,
+ std::string* canonical_address, std::string* error) {
+ host->clear();
+
+ bool ipv6 = true;
+ bool saw_port = false;
+ size_t colons = std::count(address.begin(), address.end(), ':');
+ size_t dots = std::count(address.begin(), address.end(), '.');
+ std::string port_str;
+ if (address[0] == '[') {
+ // [::1]:123
+ if (address.rfind("]:") == std::string::npos) {
+ *error = StringPrintf("bad IPv6 address '%s'", address.c_str());
+ return false;
+ }
+ *host = address.substr(1, (address.find("]:") - 1));
+ port_str = address.substr(address.rfind("]:") + 2);
+ saw_port = true;
+ } else if (dots == 0 && colons >= 2 && colons <= 7) {
+ // ::1
+ *host = address;
+ } else if (colons <= 1) {
+ // 1.2.3.4 or some.accidental.domain.com
+ ipv6 = false;
+ std::vector<std::string> pieces = Split(address, ":");
+ *host = pieces[0];
+ if (pieces.size() > 1) {
+ port_str = pieces[1];
+ saw_port = true;
+ }
+ }
+
+ if (host->empty()) {
+ *error = StringPrintf("no host in '%s'", address.c_str());
+ return false;
+ }
+
+ if (saw_port) {
+ if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 ||
+ *port > 65535) {
+ *error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(),
+ address.c_str());
+ return false;
+ }
+ }
+
+ if (canonical_address != nullptr) {
+ *canonical_address =
+ StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+ }
+
+ return true;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/parsenetaddress_test.cpp b/base/parsenetaddress_test.cpp
new file mode 100644
index 000000000..a3bfac860
--- /dev/null
+++ b/base/parsenetaddress_test.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/parsenetaddress.h"
+
+#include <gtest/gtest.h>
+
+using android::base::ParseNetAddress;
+
+TEST(ParseNetAddressTest, TestUrl) {
+ std::string canonical, host, error;
+ int port = 123;
+
+ EXPECT_TRUE(
+ ParseNetAddress("www.google.com", &host, &port, &canonical, &error));
+ EXPECT_EQ("www.google.com:123", canonical);
+ EXPECT_EQ("www.google.com", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(
+ ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error));
+ EXPECT_EQ("www.google.com:666", canonical);
+ EXPECT_EQ("www.google.com", host);
+ EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestIpv4) {
+ std::string canonical, host, error;
+ int port = 123;
+
+ EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error));
+ EXPECT_EQ("1.2.3.4:123", canonical);
+ EXPECT_EQ("1.2.3.4", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error));
+ EXPECT_EQ("1.2.3.4:666", canonical);
+ EXPECT_EQ("1.2.3.4", host);
+ EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestIpv6) {
+ std::string canonical, host, error;
+ int port = 123;
+
+ EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error));
+ EXPECT_EQ("[::1]:123", canonical);
+ EXPECT_EQ("::1", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port,
+ &canonical, &error));
+ EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical);
+ EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
+ EXPECT_EQ(123, port);
+
+ EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error));
+ EXPECT_EQ("[::1]:666", canonical);
+ EXPECT_EQ("::1", host);
+ EXPECT_EQ(666, port);
+
+ EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port,
+ &canonical, &error));
+ EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical);
+ EXPECT_EQ("fe80::200:5aee:feaa:20a2", host);
+ EXPECT_EQ(666, port);
+}
+
+TEST(ParseNetAddressTest, TestInvalidAddress) {
+ std::string canonical, host;
+ int port;
+
+ std::string failure_cases[] = {
+ // Invalid IPv4.
+ "1.2.3.4:",
+ "1.2.3.4::",
+ ":123",
+
+ // Invalid IPv6.
+ ":1",
+ "::::::::1",
+ "[::1",
+ "[::1]",
+ "[::1]:",
+ "[::1]::",
+
+ // Invalid port.
+ "1.2.3.4:-1",
+ "1.2.3.4:0",
+ "1.2.3.4:65536"
+ "1.2.3.4:hello",
+ "[::1]:-1",
+ "[::1]:0",
+ "[::1]:65536",
+ "[::1]:hello",
+ };
+
+ for (const auto& address : failure_cases) {
+ // Failure should give some non-empty error string.
+ std::string error;
+ EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error));
+ EXPECT_NE("", error);
+ }
+}
+
+// Null canonical address argument.
+TEST(ParseNetAddressTest, TestNullCanonicalAddress) {
+ std::string host, error;
+ int port = 42;
+
+ EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error));
+ EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error));
+ EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error));
+}
diff --git a/base/process.cpp b/base/process.cpp
new file mode 100644
index 000000000..b8cabf657
--- /dev/null
+++ b/base/process.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/process.h"
+
+namespace android {
+namespace base {
+
+void AllPids::PidIterator::Increment() {
+ if (!dir_) {
+ return;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir_.get())) != nullptr) {
+ pid_t pid = atoi(de->d_name);
+ if (pid != 0) {
+ pid_ = pid;
+ return;
+ }
+ }
+ pid_ = -1;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/process_test.cpp b/base/process_test.cpp
new file mode 100644
index 000000000..056f6679f
--- /dev/null
+++ b/base/process_test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/process.h"
+
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+TEST(process, find_ourselves) {
+#if defined(__linux__)
+ bool found_our_pid = false;
+ for (const auto& pid : android::base::AllPids{}) {
+ if (pid == getpid()) {
+ found_our_pid = true;
+ }
+ }
+
+ EXPECT_TRUE(found_our_pid);
+
+#endif
+}
diff --git a/base/properties.cpp b/base/properties.cpp
new file mode 100644
index 000000000..35e41a86e
--- /dev/null
+++ b/base/properties.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/properties.h"
+
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/system_properties.h>
+#include <sys/_system_properties.h>
+#endif
+
+#include <algorithm>
+#include <chrono>
+#include <limits>
+#include <map>
+#include <string>
+
+#include <android-base/parsebool.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace base {
+
+bool GetBoolProperty(const std::string& key, bool default_value) {
+ switch (ParseBool(GetProperty(key, ""))) {
+ case ParseBoolResult::kError:
+ return default_value;
+ case ParseBoolResult::kFalse:
+ return false;
+ case ParseBoolResult::kTrue:
+ return true;
+ }
+ __builtin_unreachable();
+}
+
+template <typename T>
+T GetIntProperty(const std::string& key, T default_value, T min, T max) {
+ T result;
+ std::string value = GetProperty(key, "");
+ if (!value.empty() && android::base::ParseInt(value, &result, min, max)) return result;
+ return default_value;
+}
+
+template <typename T>
+T GetUintProperty(const std::string& key, T default_value, T max) {
+ T result;
+ std::string value = GetProperty(key, "");
+ if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
+ return default_value;
+}
+
+template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t);
+template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t);
+template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t);
+template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t);
+
+template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t);
+template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t);
+template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
+template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
+
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+ g_properties[key] = value;
+ return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+ std::string property_value;
+#if defined(__BIONIC__)
+ const prop_info* pi = __system_property_find(key.c_str());
+ if (pi == nullptr) return default_value;
+
+ __system_property_read_callback(pi,
+ [](void* cookie, const char*, const char* value, unsigned) {
+ auto property_value = reinterpret_cast<std::string*>(cookie);
+ *property_value = value;
+ },
+ &property_value);
+#else
+ auto it = g_properties.find(key);
+ if (it == g_properties.end()) return default_value;
+ property_value = it->second;
+#endif
+ // If the property exists but is empty, also return the default value.
+ // Since we can't remove system properties, "empty" is traditionally
+ // the same as "missing" (this was true for cutils' property_get).
+ return property_value.empty() ? default_value : property_value;
+}
+
+bool SetProperty(const std::string& key, const std::string& value) {
+ return (__system_property_set(key.c_str(), value.c_str()) == 0);
+}
+
+#if defined(__BIONIC__)
+
+struct WaitForPropertyData {
+ bool done;
+ const std::string* expected_value;
+ unsigned last_read_serial;
+};
+
+static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
+ WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
+ if (*data->expected_value == value) {
+ data->done = true;
+ } else {
+ data->last_read_serial = serial;
+ }
+}
+
+// TODO: chrono_utils?
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
+ auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
+ ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
+ ts.tv_nsec = ns.count();
+}
+
+// TODO: boot_clock?
+using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
+
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+ const AbsTime& start_time) {
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed >= relative_timeout) {
+ ts = { 0, 0 };
+ } else {
+ auto remaining_timeout = relative_timeout - time_elapsed;
+ DurationToTimeSpec(ts, remaining_timeout);
+ }
+}
+
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Sets absolute_timeout which represents absolute time for the timeout.
+// Returns nullptr on timeout.
+static const prop_info* WaitForPropertyCreation(const std::string& key,
+ const std::chrono::milliseconds& relative_timeout,
+ const AbsTime& start_time) {
+ // Find the property's prop_info*.
+ const prop_info* pi;
+ unsigned global_serial = 0;
+ while ((pi = __system_property_find(key.c_str())) == nullptr) {
+ // The property doesn't even exist yet.
+ // Wait for a global change and then look again.
+ timespec ts;
+ UpdateTimeSpec(ts, relative_timeout, start_time);
+ if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
+ }
+ return pi;
+}
+
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+ const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
+ if (pi == nullptr) return false;
+
+ WaitForPropertyData data;
+ data.expected_value = &expected_value;
+ data.done = false;
+ while (true) {
+ timespec ts;
+ // Check whether the property has the value we're looking for?
+ __system_property_read_callback(pi, WaitForPropertyCallback, &data);
+ if (data.done) return true;
+
+ // It didn't, so wait for the property to change before checking again.
+ UpdateTimeSpec(ts, relative_timeout, start_time);
+ uint32_t unused;
+ if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
+ }
+}
+
+bool WaitForPropertyCreation(const std::string& key,
+ std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+ return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
+}
+
+CachedProperty::CachedProperty(const char* property_name)
+ : property_name_(property_name),
+ prop_info_(nullptr),
+ cached_area_serial_(0),
+ cached_property_serial_(0),
+ is_read_only_(android::base::StartsWith(property_name, "ro.")),
+ read_only_property_(nullptr) {
+ static_assert(sizeof(cached_value_) == PROP_VALUE_MAX);
+}
+
+const char* CachedProperty::Get(bool* changed) {
+ std::optional<uint32_t> initial_property_serial_ = cached_property_serial_;
+
+ // Do we have a `struct prop_info` yet?
+ if (prop_info_ == nullptr) {
+ // `__system_property_find` is expensive, so only retry if a property
+ // has been created since last time we checked.
+ uint32_t property_area_serial = __system_property_area_serial();
+ if (property_area_serial != cached_area_serial_) {
+ prop_info_ = __system_property_find(property_name_.c_str());
+ cached_area_serial_ = property_area_serial;
+ }
+ }
+
+ if (prop_info_ != nullptr) {
+ // Only bother re-reading the property if it's actually changed since last time.
+ uint32_t property_serial = __system_property_serial(prop_info_);
+ if (property_serial != cached_property_serial_) {
+ __system_property_read_callback(
+ prop_info_,
+ [](void* data, const char*, const char* value, uint32_t serial) {
+ CachedProperty* instance = reinterpret_cast<CachedProperty*>(data);
+ instance->cached_property_serial_ = serial;
+ // Read only properties can be larger than PROP_VALUE_MAX, but also never change value
+ // or location, thus we return the pointer from the shared memory directly.
+ if (instance->is_read_only_) {
+ instance->read_only_property_ = value;
+ } else {
+ strlcpy(instance->cached_value_, value, PROP_VALUE_MAX);
+ }
+ },
+ this);
+ }
+ }
+
+ if (changed) {
+ *changed = cached_property_serial_ != initial_property_serial_;
+ }
+
+ if (is_read_only_) {
+ return read_only_property_;
+ } else {
+ return cached_value_;
+ }
+}
+
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
new file mode 100644
index 000000000..c30c41e8a
--- /dev/null
+++ b/base/properties_test.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/properties.h"
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <chrono>
+#include <string>
+#include <thread>
+
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
+
+TEST(properties, smoke) {
+ android::base::SetProperty("debug.libbase.property_test", "hello");
+
+ std::string s = android::base::GetProperty("debug.libbase.property_test", "");
+ ASSERT_EQ("hello", s);
+
+ android::base::SetProperty("debug.libbase.property_test", "world");
+ s = android::base::GetProperty("debug.libbase.property_test", "");
+ ASSERT_EQ("world", s);
+
+ s = android::base::GetProperty("this.property.does.not.exist", "");
+ ASSERT_EQ("", s);
+
+ s = android::base::GetProperty("this.property.does.not.exist", "default");
+ ASSERT_EQ("default", s);
+}
+
+TEST(properties, empty) {
+ // Because you can't delete a property, people "delete" them by
+ // setting them to the empty string. In that case we'd want to
+ // keep the default value (like cutils' property_get did).
+ android::base::SetProperty("debug.libbase.property_test", "");
+ std::string s = android::base::GetProperty("debug.libbase.property_test", "default");
+ ASSERT_EQ("default", s);
+}
+
+static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) {
+ android::base::SetProperty("debug.libbase.property_test", value.c_str());
+ ASSERT_EQ(expected, android::base::GetBoolProperty("debug.libbase.property_test", default_value));
+}
+
+TEST(properties, GetBoolProperty_true) {
+ CheckGetBoolProperty(true, "1", false);
+ CheckGetBoolProperty(true, "y", false);
+ CheckGetBoolProperty(true, "yes", false);
+ CheckGetBoolProperty(true, "on", false);
+ CheckGetBoolProperty(true, "true", false);
+}
+
+TEST(properties, GetBoolProperty_false) {
+ CheckGetBoolProperty(false, "0", true);
+ CheckGetBoolProperty(false, "n", true);
+ CheckGetBoolProperty(false, "no", true);
+ CheckGetBoolProperty(false, "off", true);
+ CheckGetBoolProperty(false, "false", true);
+}
+
+TEST(properties, GetBoolProperty_default) {
+ CheckGetBoolProperty(true, "burp", true);
+ CheckGetBoolProperty(false, "burp", false);
+}
+
+template <typename T> void CheckGetIntProperty() {
+ // Positive and negative.
+ android::base::SetProperty("debug.libbase.property_test", "-12");
+ EXPECT_EQ(T(-12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(12), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
+
+ // Default value.
+ android::base::SetProperty("debug.libbase.property_test", "");
+ EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45));
+
+ // Bounds checks.
+ android::base::SetProperty("debug.libbase.property_test", "0");
+ EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+ android::base::SetProperty("debug.libbase.property_test", "1");
+ EXPECT_EQ(T(1), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+ android::base::SetProperty("debug.libbase.property_test", "2");
+ EXPECT_EQ(T(2), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+ android::base::SetProperty("debug.libbase.property_test", "3");
+ EXPECT_EQ(T(45), android::base::GetIntProperty<T>("debug.libbase.property_test", 45, 1, 2));
+}
+
+template <typename T> void CheckGetUintProperty() {
+ // Positive.
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
+
+ // Default value.
+ android::base::SetProperty("debug.libbase.property_test", "");
+ EXPECT_EQ(T(45), android::base::GetUintProperty<T>("debug.libbase.property_test", 45));
+
+ // Bounds checks.
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(12), android::base::GetUintProperty<T>("debug.libbase.property_test", 33, 22));
+ android::base::SetProperty("debug.libbase.property_test", "12");
+ EXPECT_EQ(T(5), android::base::GetUintProperty<T>("debug.libbase.property_test", 5, 10));
+}
+
+TEST(properties, GetIntProperty_int8_t) { CheckGetIntProperty<int8_t>(); }
+TEST(properties, GetIntProperty_int16_t) { CheckGetIntProperty<int16_t>(); }
+TEST(properties, GetIntProperty_int32_t) { CheckGetIntProperty<int32_t>(); }
+TEST(properties, GetIntProperty_int64_t) { CheckGetIntProperty<int64_t>(); }
+
+TEST(properties, GetUintProperty_uint8_t) { CheckGetUintProperty<uint8_t>(); }
+TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
+TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
+TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
+
+TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ std::this_thread::sleep_for(100ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
+ thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
+
+TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
+ auto t0 = std::chrono::steady_clock::now();
+ ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
+ 200ms));
+ auto t1 = std::chrono::steady_clock::now();
+
+ ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+ // Upper bounds on timing are inherently flaky, but let's try...
+ ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
+
+TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Test that this does not immediately return false due to overflow issues with the timeout.
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+ thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Assert that this immediately returns with a negative timeout
+ ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+ thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
+
+TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
+ std::thread thread([&]() {
+ std::this_thread::sleep_for(100ms);
+ android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
+ });
+
+ ASSERT_TRUE(android::base::WaitForPropertyCreation(
+ "debug.libbase.WaitForPropertyCreation_test", 1s));
+ thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
+
+TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
+ auto t0 = std::chrono::steady_clock::now();
+ ASSERT_FALSE(android::base::WaitForPropertyCreation(
+ "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
+ auto t1 = std::chrono::steady_clock::now();
+
+ ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+ // Upper bounds on timing are inherently flaky, but let's try...
+ ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
+
+TEST(properties, CachedProperty) {
+#if defined(__BIONIC__)
+ android::base::CachedProperty cached_property("debug.libbase.CachedProperty_test");
+ bool changed;
+ cached_property.Get(&changed);
+
+ android::base::SetProperty("debug.libbase.CachedProperty_test", "foo");
+ ASSERT_STREQ("foo", cached_property.Get(&changed));
+ ASSERT_TRUE(changed);
+
+ ASSERT_STREQ("foo", cached_property.Get(&changed));
+ ASSERT_FALSE(changed);
+
+ android::base::SetProperty("debug.libbase.CachedProperty_test", "bar");
+ ASSERT_STREQ("bar", cached_property.Get(&changed));
+ ASSERT_TRUE(changed);
+
+ ASSERT_STREQ("bar", cached_property.Get(&changed));
+ ASSERT_FALSE(changed);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
+}
diff --git a/base/result_test.cpp b/base/result_test.cpp
new file mode 100644
index 000000000..c0ac0fdaf
--- /dev/null
+++ b/base/result_test.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/result.h"
+
+#include "errno.h"
+
+#include <istream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace base {
+
+TEST(result, result_accessors) {
+ Result<std::string> result = "success";
+ ASSERT_RESULT_OK(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("success", *result);
+ EXPECT_EQ("success", result.value());
+
+ EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+ ASSERT_TRUE(Result<std::string>("success").ok());
+ ASSERT_TRUE(Result<std::string>("success").has_value());
+
+ EXPECT_EQ("success", *Result<std::string>("success"));
+ EXPECT_EQ("success", Result<std::string>("success").value());
+
+ EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_void) {
+ Result<void> ok = {};
+ EXPECT_RESULT_OK(ok);
+ ok.value(); // should not crash
+ ASSERT_DEATH(ok.error(), "");
+
+ Result<void> fail = Error() << "failure" << 1;
+ EXPECT_FALSE(fail.ok());
+ EXPECT_EQ("failure1", fail.error().message());
+ EXPECT_EQ(0, fail.error().code());
+ EXPECT_TRUE(ok != fail);
+ ASSERT_DEATH(fail.value(), "");
+
+ auto test = [](bool ok) -> Result<void> {
+ if (ok) return {};
+ else return Error() << "failure" << 1;
+ };
+ EXPECT_TRUE(test(true).ok());
+ EXPECT_FALSE(test(false).ok());
+ test(true).value(); // should not crash
+ ASSERT_DEATH(test(true).error(), "");
+ ASSERT_DEATH(test(false).value(), "");
+ EXPECT_EQ("failure1", test(false).error().message());
+}
+
+TEST(result, result_error) {
+ Result<void> result = Error() << "failure" << 1;
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error().code());
+ EXPECT_EQ("failure1", result.error().message());
+}
+
+TEST(result, result_error_empty) {
+ Result<void> result = Error();
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error().code());
+ EXPECT_EQ("", result.error().message());
+}
+
+TEST(result, result_error_rvalue) {
+ // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+ // Under the hood, they are an intermediate class that can be implicitly constructed into a
+ // Result<T>. This is needed both to create the ostream and because Error() itself, by
+ // definition will not know what the type, T, of the underlying Result<T> object that it would
+ // create is.
+
+ auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
+ ASSERT_FALSE(MakeRvalueErrorResult().ok());
+ ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+ EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
+ EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
+}
+
+TEST(result, result_errno_error) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<void> result = ErrnoError() << "failure" << 1;
+
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_errno_error_no_text) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<void> result = ErrnoError();
+
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ(strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_error_from_other_result) {
+ auto error_text = "test error"s;
+ Result<void> result = Error() << error_text;
+
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = result.error();
+
+ ASSERT_FALSE(result2.ok());
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result2.error().code());
+ EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_error_through_ostream) {
+ auto error_text = "test error"s;
+ Result<void> result = Error() << error_text;
+
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2.ok());
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result2.error().code());
+ EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+ auto error_text = "test error"s;
+ constexpr int test_errno = 6;
+ errno = 6;
+ Result<void> result = ErrnoError() << error_text;
+
+ errno = 0;
+
+ ASSERT_FALSE(result.ok());
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2.ok());
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(test_errno, result2.error().code());
+ EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
+}
+
+TEST(result, constructor_forwarding) {
+ auto result = Result<std::string>(std::in_place, 5, 'a');
+
+ ASSERT_RESULT_OK(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+};
+
+TEST(result, no_copy_on_return) {
+ // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+ // then those parameters are forwarded to the construction of Result<T>.
+
+ // If returning an prvalue or xvalue, it will be move constructed during the construction of
+ // Result<T>.
+
+ // This check ensures that that is the case, and particularly that no copy constructors
+ // are called.
+
+ auto result1 = ReturnConstructorTracker("");
+ ASSERT_RESULT_OK(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result2 = ReturnConstructorTracker("test2");
+ ASSERT_RESULT_OK(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result3 = ReturnConstructorTracker("test3");
+ ASSERT_RESULT_OK(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor. This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+ auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
+ auto result = return_result_result_with_success();
+ ASSERT_RESULT_OK(result);
+ ASSERT_RESULT_OK(*result);
+
+ auto inner_result = result.value();
+ ASSERT_RESULT_OK(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+ auto return_result_result_with_error = []() -> Result<Result<void>> {
+ return Result<void>(ResultError("failure string", 6));
+ };
+ auto result = return_result_result_with_error();
+ ASSERT_RESULT_OK(result);
+ ASSERT_FALSE(result->ok());
+ EXPECT_EQ("failure string", (*result).error().message());
+ EXPECT_EQ(6, (*result).error().code());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+ struct TestStruct {
+ TestStruct(int value) : value_(value) {}
+ TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+ int value_;
+ };
+
+ auto return_test_struct = []() -> Result<TestStruct> {
+ return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
+ };
+
+ auto result = return_test_struct();
+ ASSERT_RESULT_OK(result);
+ EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+ Result<std::string> result = Error();
+ ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+ Result<std::string> result = "success";
+ ASSERT_DEATH(result.error(), "");
+}
+
+template <class CharT>
+std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
+ errno = 2;
+ return ss;
+}
+
+TEST(result, preserve_errno) {
+ errno = 1;
+ int old_errno = errno;
+ Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
+ ASSERT_FALSE(result.ok());
+ EXPECT_EQ(old_errno, errno);
+
+ errno = 1;
+ old_errno = errno;
+ Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
+ ASSERT_FALSE(result2.ok());
+ EXPECT_EQ(old_errno, errno);
+ EXPECT_EQ(old_errno, result2.error().code());
+}
+
+TEST(result, error_with_fmt) {
+ Result<int> result = Errorf("{} {}!", "hello", "world");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("{} {}!", std::string("hello"), std::string("world"));
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("{1} {0}!", "world", "hello");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("hello world!");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ Result<int> result2 = Errorf("error occurred with {}", result.error());
+ EXPECT_EQ("error occurred with hello world!", result2.error().message());
+
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ result = ErrnoErrorf("{} {}!", "hello", "world");
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, error_with_fmt_carries_errno) {
+ constexpr int inner_errno = 6;
+ errno = inner_errno;
+ Result<int> inner_result = ErrnoErrorf("inner failure");
+ errno = 0;
+ EXPECT_EQ(inner_errno, inner_result.error().code());
+
+ // outer_result is created with Errorf, but its error code is got from inner_result.
+ Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
+ EXPECT_EQ(inner_errno, outer_result.error().code());
+ EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
+ outer_result.error().message());
+
+ // now both result objects are created with ErrnoErrorf. errno from the inner_result
+ // is not passed to outer_result.
+ constexpr int outer_errno = 10;
+ errno = outer_errno;
+ outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
+ EXPECT_EQ(outer_errno, outer_result.error().code());
+ EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
+ strerror(outer_errno),
+ outer_result.error().message());
+}
+
+TEST(result, errno_chaining_multiple) {
+ constexpr int errno1 = 6;
+ errno = errno1;
+ Result<int> inner1 = ErrnoErrorf("error1");
+
+ constexpr int errno2 = 10;
+ errno = errno2;
+ Result<int> inner2 = ErrnoErrorf("error2");
+
+ // takes the error code of inner2 since its the last one.
+ Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
+ EXPECT_EQ(errno2, outer.error().code());
+ EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
+ outer.error().message());
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 000000000..9236d7b78
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/scopeguard.h"
+
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ }
+ ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ scopeguard.Disable();
+ }
+ ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+ int guarded_var = true;
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+ EXPECT_FALSE(scopeguard.active());
+ ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, vector) {
+ int guarded_var = 0;
+ {
+ std::vector<android::base::ScopeGuard<std::function<void()>>> scopeguards;
+ scopeguards.emplace_back(android::base::make_scope_guard(
+ std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
+ scopeguards.emplace_back(android::base::make_scope_guard(
+ std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
+ }
+ ASSERT_EQ(guarded_var, 2);
+}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
new file mode 100644
index 000000000..e83ab1316
--- /dev/null
+++ b/base/stringprintf.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/stringprintf.h"
+
+#include <stdio.h>
+
+#include <string>
+
+namespace android {
+namespace base {
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ char space[1024] __attribute__((__uninitialized__));
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, sizeof(space), format, backup_ap);
+ va_end(backup_ap);
+
+ if (result < static_cast<int>(sizeof(space))) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, result);
+ return;
+ }
+
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ }
+
+ // Increase the buffer size to the size requested by vsnprintf,
+ // plus one for the closing \0.
+ int length = result + 1;
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < length) {
+ // It fit
+ dst->append(buf, result);
+ }
+ delete[] buf;
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string result;
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
new file mode 100644
index 000000000..fc009b1d7
--- /dev/null
+++ b/base/stringprintf_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/stringprintf.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+TEST(StringPrintfTest, HexSizeT) {
+ size_t size = 0x00107e59;
+ EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
+ EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
+}
+
+TEST(StringPrintfTest, StringAppendF) {
+ std::string s("a");
+ android::base::StringAppendF(&s, "b");
+ EXPECT_EQ("ab", s);
+}
+
+TEST(StringPrintfTest, Errno) {
+ errno = 123;
+ android::base::StringPrintf("hello %s", "world");
+ EXPECT_EQ(123, errno);
+}
+
+void TestN(size_t n) {
+ char* buf = new char[n + 1];
+ memset(buf, 'x', n);
+ buf[n] = '\0';
+ std::string s(android::base::StringPrintf("%s", buf));
+ EXPECT_EQ(buf, s);
+ delete[] buf;
+}
+
+TEST(StringPrintfTest, At1023) {
+ TestN(1023);
+}
+
+TEST(StringPrintfTest, At1024) {
+ TestN(1024);
+}
+
+TEST(StringPrintfTest, At1025) {
+ TestN(1025);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
new file mode 100644
index 000000000..40b2bf270
--- /dev/null
+++ b/base/strings.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/strings.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace base {
+
+#define CHECK_NE(a, b) \
+ if ((a) == (b)) abort();
+
+std::vector<std::string> Split(const std::string& s,
+ const std::string& delimiters) {
+ CHECK_NE(delimiters.size(), 0U);
+
+ std::vector<std::string> result;
+
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, found - base));
+ if (found == s.npos) break;
+ base = found + 1;
+ }
+
+ return result;
+}
+
+std::string Trim(const std::string& s) {
+ std::string result;
+
+ if (s.size() == 0) {
+ return result;
+ }
+
+ size_t start_index = 0;
+ size_t end_index = s.size() - 1;
+
+ // Skip initial whitespace.
+ while (start_index < s.size()) {
+ if (!isspace(s[start_index])) {
+ break;
+ }
+ start_index++;
+ }
+
+ // Skip terminating whitespace.
+ while (end_index >= start_index) {
+ if (!isspace(s[end_index])) {
+ break;
+ }
+ end_index--;
+ }
+
+ // All spaces, no beef.
+ if (end_index < start_index) {
+ return "";
+ }
+ // Start_index is the first non-space, end_index is the last one.
+ return s.substr(start_index, end_index - start_index + 1);
+}
+
+// These cases are probably the norm, so we mark them extern in the header to
+// aid compile time and binary size.
+template std::string Join(const std::vector<std::string>&, char);
+template std::string Join(const std::vector<const char*>&, char);
+template std::string Join(const std::vector<std::string>&, const std::string&);
+template std::string Join(const std::vector<const char*>&, const std::string&);
+
+bool StartsWith(std::string_view s, std::string_view prefix) {
+ return s.substr(0, prefix.size()) == prefix;
+}
+
+bool StartsWith(std::string_view s, char prefix) {
+ return !s.empty() && s.front() == prefix;
+}
+
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
+ return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
+}
+
+bool EndsWith(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
+}
+
+bool EndsWith(std::string_view s, char suffix) {
+ return !s.empty() && s.back() == suffix;
+}
+
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() &&
+ strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
+}
+
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
+ return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
+}
+
+std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
+ bool all) {
+ if (from.empty()) return std::string(s);
+
+ std::string result;
+ std::string_view::size_type start_pos = 0;
+ do {
+ std::string_view::size_type pos = s.find(from, start_pos);
+ if (pos == std::string_view::npos) break;
+
+ result.append(s.data() + start_pos, pos - start_pos);
+ result.append(to.data(), to.size());
+
+ start_pos = pos + from.size();
+ } while (all);
+ result.append(s.data() + start_pos, s.size() - start_pos);
+ return result;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
new file mode 100644
index 000000000..5ae309446
--- /dev/null
+++ b/base/strings_test.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/strings.h"
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+#include <set>
+#include <unordered_set>
+
+TEST(strings, split_empty) {
+ std::vector<std::string> parts = android::base::Split("", ",");
+ ASSERT_EQ(1U, parts.size());
+ ASSERT_EQ("", parts[0]);
+}
+
+TEST(strings, split_single) {
+ std::vector<std::string> parts = android::base::Split("foo", ",");
+ ASSERT_EQ(1U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+}
+
+TEST(strings, split_simple) {
+ std::vector<std::string> parts = android::base::Split("foo,bar,baz", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("baz", parts[2]);
+}
+
+TEST(strings, split_with_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo,,bar", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("", parts[1]);
+ ASSERT_EQ("bar", parts[2]);
+}
+
+TEST(strings, split_with_trailing_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("", parts[2]);
+}
+
+TEST(strings, split_null_char) {
+ std::vector<std::string> parts =
+ android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
+ ASSERT_EQ(2U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+}
+
+TEST(strings, split_any) {
+ std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("bar", parts[1]);
+ ASSERT_EQ("baz", parts[2]);
+}
+
+TEST(strings, split_any_with_empty_part) {
+ std::vector<std::string> parts = android::base::Split("foo:,bar", ",:");
+ ASSERT_EQ(3U, parts.size());
+ ASSERT_EQ("foo", parts[0]);
+ ASSERT_EQ("", parts[1]);
+ ASSERT_EQ("bar", parts[2]);
+}
+
+TEST(strings, trim_empty) {
+ ASSERT_EQ("", android::base::Trim(""));
+}
+
+TEST(strings, trim_already_trimmed) {
+ ASSERT_EQ("foo", android::base::Trim("foo"));
+}
+
+TEST(strings, trim_left) {
+ ASSERT_EQ("foo", android::base::Trim(" foo"));
+}
+
+TEST(strings, trim_right) {
+ ASSERT_EQ("foo", android::base::Trim("foo "));
+}
+
+TEST(strings, trim_both) {
+ ASSERT_EQ("foo", android::base::Trim(" foo "));
+}
+
+TEST(strings, trim_no_trim_middle) {
+ ASSERT_EQ("foo bar", android::base::Trim("foo bar"));
+}
+
+TEST(strings, trim_other_whitespace) {
+ ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f"));
+}
+
+TEST(strings, join_nothing) {
+ std::vector<std::string> list = {};
+ ASSERT_EQ("", android::base::Join(list, ','));
+}
+
+TEST(strings, join_single) {
+ std::vector<std::string> list = {"foo"};
+ ASSERT_EQ("foo", android::base::Join(list, ','));
+}
+
+TEST(strings, join_simple) {
+ std::vector<std::string> list = {"foo", "bar", "baz"};
+ ASSERT_EQ("foo,bar,baz", android::base::Join(list, ','));
+}
+
+TEST(strings, join_separator_in_vector) {
+ std::vector<std::string> list = {",", ","};
+ ASSERT_EQ(",,,", android::base::Join(list, ','));
+}
+
+TEST(strings, join_simple_ints) {
+ std::set<int> list = {1, 2, 3};
+ ASSERT_EQ("1,2,3", android::base::Join(list, ','));
+}
+
+TEST(strings, join_unordered_set) {
+ std::unordered_set<int> list = {1, 2};
+ ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
+ "2,1" == android::base::Join(list, ','));
+}
+
+TEST(strings, StartsWith_empty) {
+ ASSERT_FALSE(android::base::StartsWith("", "foo"));
+ ASSERT_TRUE(android::base::StartsWith("", ""));
+}
+
+TEST(strings, StartsWithIgnoreCase_empty) {
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("", "foo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("", ""));
+}
+
+TEST(strings, StartsWith_simple) {
+ ASSERT_TRUE(android::base::StartsWith("foo", ""));
+ ASSERT_TRUE(android::base::StartsWith("foo", "f"));
+ ASSERT_TRUE(android::base::StartsWith("foo", "fo"));
+ ASSERT_TRUE(android::base::StartsWith("foo", "foo"));
+}
+
+TEST(strings, StartsWithIgnoreCase_simple) {
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", ""));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "f"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "F"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Fo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "foO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "fOO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "Foo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FoO"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOo"));
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("foo", "FOO"));
+}
+
+TEST(strings, StartsWith_prefix_too_long) {
+ ASSERT_FALSE(android::base::StartsWith("foo", "foobar"));
+}
+
+TEST(strings, StartsWithIgnoreCase_prefix_too_long) {
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "foobar"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foo", "FOOBAR"));
+}
+
+TEST(strings, StartsWith_contains_prefix) {
+ ASSERT_FALSE(android::base::StartsWith("foobar", "oba"));
+ ASSERT_FALSE(android::base::StartsWith("foobar", "bar"));
+}
+
+TEST(strings, StartsWithIgnoreCase_contains_prefix) {
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "oba"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "OBA"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "bar"));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
+}
+
+TEST(strings, StartsWith_char) {
+ ASSERT_FALSE(android::base::StartsWith("", 'f'));
+ ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+ ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
+TEST(strings, EndsWith_empty) {
+ ASSERT_FALSE(android::base::EndsWith("", "foo"));
+ ASSERT_TRUE(android::base::EndsWith("", ""));
+}
+
+TEST(strings, EndsWithIgnoreCase_empty) {
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "foo"));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("", "FOO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("", ""));
+}
+
+TEST(strings, EndsWith_simple) {
+ ASSERT_TRUE(android::base::EndsWith("foo", ""));
+ ASSERT_TRUE(android::base::EndsWith("foo", "o"));
+ ASSERT_TRUE(android::base::EndsWith("foo", "oo"));
+ ASSERT_TRUE(android::base::EndsWith("foo", "foo"));
+}
+
+TEST(strings, EndsWithIgnoreCase_simple) {
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", ""));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "o"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "O"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "oO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Oo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "OO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "foO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "fOO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "Foo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FoO"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOo"));
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("foo", "FOO"));
+}
+
+TEST(strings, EndsWith_prefix_too_long) {
+ ASSERT_FALSE(android::base::EndsWith("foo", "foobar"));
+}
+
+TEST(strings, EndsWithIgnoreCase_prefix_too_long) {
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "foobar"));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foo", "FOOBAR"));
+}
+
+TEST(strings, EndsWith_contains_prefix) {
+ ASSERT_FALSE(android::base::EndsWith("foobar", "oba"));
+ ASSERT_FALSE(android::base::EndsWith("foobar", "foo"));
+}
+
+TEST(strings, EndsWithIgnoreCase_contains_prefix) {
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "OBA"));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
+}
+
+TEST(strings, StartsWith_std_string) {
+ ASSERT_TRUE(android::base::StartsWith("hello", std::string{"hell"}));
+ ASSERT_FALSE(android::base::StartsWith("goodbye", std::string{"hell"}));
+}
+
+TEST(strings, StartsWithIgnoreCase_std_string) {
+ ASSERT_TRUE(android::base::StartsWithIgnoreCase("HeLlO", std::string{"hell"}));
+ ASSERT_FALSE(android::base::StartsWithIgnoreCase("GoOdByE", std::string{"hell"}));
+}
+
+TEST(strings, EndsWith_std_string) {
+ ASSERT_TRUE(android::base::EndsWith("hello", std::string{"lo"}));
+ ASSERT_FALSE(android::base::EndsWith("goodbye", std::string{"lo"}));
+}
+
+TEST(strings, EndsWithIgnoreCase_std_string) {
+ ASSERT_TRUE(android::base::EndsWithIgnoreCase("HeLlO", std::string{"lo"}));
+ ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
+}
+
+TEST(strings, EndsWith_char) {
+ ASSERT_FALSE(android::base::EndsWith("", 'o'));
+ ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+ ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
+TEST(strings, EqualsIgnoreCase) {
+ ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
+ ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
+ ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
+ ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
+}
+
+TEST(strings, ubsan_28729303) {
+ android::base::Split("/dev/null", ":");
+}
+
+TEST(strings, ConsumePrefix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
+ ASSERT_EQ("bar", s);
+}
+
+TEST(strings, ConsumeSuffix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
+ ASSERT_EQ("foo", s);
+}
+
+TEST(strings, StringReplace_false) {
+ // No change.
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
+ ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
+
+ // Equal lengths.
+ ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
+ ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
+ ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
+
+ // Longer replacement.
+ ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
+ ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
+ ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
+
+ // Shorter replacement.
+ ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
+ ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
+ ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
+}
+
+TEST(strings, StringReplace_true) {
+ // No change.
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
+ ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
+
+ // Equal lengths.
+ ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
+ ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
+ ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
+
+ // Longer replacement.
+ ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
+ ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
+ ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
+
+ // Shorter replacement.
+ ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
+ ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
+}
diff --git a/base/test_main.cpp b/base/test_main.cpp
new file mode 100644
index 000000000..7fa6a8425
--- /dev/null
+++ b/base/test_main.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "android-base/logging.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
new file mode 100644
index 000000000..36b4cdfa4
--- /dev/null
+++ b/base/test_utils.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
+ Start();
+}
+
+CapturedStdFd::~CapturedStdFd() {
+ if (old_fd_ != -1) {
+ Stop();
+ }
+}
+
+int CapturedStdFd::fd() const {
+ return temp_file_.fd;
+}
+
+std::string CapturedStdFd::str() {
+ std::string result;
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+ android::base::ReadFdToString(fd(), &result);
+ return result;
+}
+
+void CapturedStdFd::Reset() {
+ // Do not reset while capturing.
+ CHECK_EQ(-1, old_fd_);
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+ CHECK_EQ(0, ftruncate(fd(), 0));
+}
+
+void CapturedStdFd::Start() {
+#if defined(_WIN32)
+ // On Windows, stderr is often buffered, so make sure it is unbuffered so
+ // that we can immediately read back what was written to stderr.
+ if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, nullptr, _IONBF, 0));
+#endif
+ old_fd_ = dup(std_fd_);
+ CHECK_NE(-1, old_fd_);
+ CHECK_NE(-1, dup2(fd(), std_fd_));
+}
+
+void CapturedStdFd::Stop() {
+ CHECK_NE(-1, old_fd_);
+ CHECK_NE(-1, dup2(old_fd_, std_fd_));
+ close(old_fd_);
+ old_fd_ = -1;
+ // Note: cannot restore prior setvbuf() setting.
+}
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
new file mode 100644
index 000000000..15a79dd9d
--- /dev/null
+++ b/base/test_utils_test.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "android-base/test_utils.h"
+
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+TEST(TestUtilsTest, AssertMatch) {
+ ASSERT_MATCH("foobar", R"(fo+baz?r)");
+ EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, AssertNotMatch) {
+ ASSERT_NOT_MATCH("foobar", R"(foobaz)");
+ EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, ExpectMatch) {
+ EXPECT_MATCH("foobar", R"(fo+baz?r)");
+ EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, ExpectNotMatch) {
+ EXPECT_NOT_MATCH("foobar", R"(foobaz)");
+ EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, CaptureStdout_smoke) {
+ CapturedStdout cap;
+ printf("This should be captured.\n");
+ cap.Stop();
+ printf("This will not be captured.\n");
+ ASSERT_EQ("This should be captured.\n", cap.str());
+
+ cap.Start();
+ printf("And this text should be captured too.\n");
+ cap.Stop();
+ ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+ printf("Still not going to be captured.\n");
+ cap.Reset();
+ cap.Start();
+ printf("Only this will be captured.\n");
+ ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
+TEST(TestUtilsTest, CaptureStderr_smoke) {
+ CapturedStderr cap;
+ fprintf(stderr, "This should be captured.\n");
+ cap.Stop();
+ fprintf(stderr, "This will not be captured.\n");
+ ASSERT_EQ("This should be captured.\n", cap.str());
+
+ cap.Start();
+ fprintf(stderr, "And this text should be captured too.\n");
+ cap.Stop();
+ ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+ fprintf(stderr, "Still not going to be captured.\n");
+ cap.Reset();
+ cap.Start();
+ fprintf(stderr, "Only this will be captured.\n");
+ ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/threads.cpp b/base/threads.cpp
new file mode 100644
index 000000000..48f6197ef
--- /dev/null
+++ b/base/threads.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/threads.h>
+
+#include <stdint.h>
+#include <unistd.h>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+namespace android {
+namespace base {
+
+uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+ return gettid();
+#elif defined(__APPLE__)
+ uint64_t tid;
+ pthread_threadid_np(NULL, &tid);
+ return tid;
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+
+} // namespace base
+} // namespace android
+
+#if defined(__GLIBC__)
+int tgkill(int tgid, int tid, int sig) {
+ return syscall(__NR_tgkill, tgid, tid, sig);
+}
+#endif
diff --git a/base/utf8.cpp b/base/utf8.cpp
new file mode 100644
index 000000000..adb46d09c
--- /dev/null
+++ b/base/utf8.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <windows.h>
+
+#include "android-base/utf8.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <string>
+
+#include "android-base/logging.h"
+
+namespace android {
+namespace base {
+
+// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
+static void SetErrnoFromLastError() {
+ switch (GetLastError()) {
+ case ERROR_NO_UNICODE_TRANSLATION:
+ errno = EILSEQ;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+}
+
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
+ utf8->clear();
+
+ if (size == 0) {
+ return true;
+ }
+
+ // TODO: Consider using std::wstring_convert once libcxx is supported on
+ // Windows.
+
+ // Only Vista or later has this flag that causes WideCharToMultiByte() to
+ // return an error on invalid characters.
+ const DWORD flags =
+#if (WINVER >= 0x0600)
+ WC_ERR_INVALID_CHARS;
+#else
+ 0;
+#endif
+
+ const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+ NULL, 0, NULL, NULL);
+ if (chars_required <= 0) {
+ SetErrnoFromLastError();
+ return false;
+ }
+
+ // This could potentially throw a std::bad_alloc exception.
+ utf8->resize(chars_required);
+
+ const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+ &(*utf8)[0], chars_required, NULL,
+ NULL);
+ if (result != chars_required) {
+ SetErrnoFromLastError();
+ CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
+ << " chars to buffer of " << chars_required << " chars";
+ utf8->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
+ // Compute string length of NULL-terminated string with wcslen().
+ return WideToUTF8(utf16, wcslen(utf16), utf8);
+}
+
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
+ // Use the stored length of the string which allows embedded NULL characters
+ // to be converted.
+ return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
+}
+
+// Internal helper function that takes MultiByteToWideChar() flags.
+static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
+ const DWORD flags) {
+ utf16->clear();
+
+ if (size == 0) {
+ return true;
+ }
+
+ // TODO: Consider using std::wstring_convert once libcxx is supported on
+ // Windows.
+ const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+ NULL, 0);
+ if (chars_required <= 0) {
+ SetErrnoFromLastError();
+ return false;
+ }
+
+ // This could potentially throw a std::bad_alloc exception.
+ utf16->resize(chars_required);
+
+ const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+ &(*utf16)[0], chars_required);
+ if (result != chars_required) {
+ SetErrnoFromLastError();
+ CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
+ << " chars to buffer of " << chars_required << " chars";
+ utf16->clear();
+ return false;
+ }
+
+ return true;
+}
+
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
+ // If strictly interpreting as UTF-8 succeeds, return success.
+ if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
+ return true;
+ }
+
+ const int saved_errno = errno;
+
+ // Fallback to non-strict interpretation, allowing invalid characters and
+ // converting as best as possible, and return false to signify a problem.
+ (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
+ errno = saved_errno;
+ return false;
+}
+
+bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
+ // Compute string length of NULL-terminated string with strlen().
+ return UTF8ToWide(utf8, strlen(utf8), utf16);
+}
+
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
+ // Use the stored length of the string which allows embedded NULL characters
+ // to be converted.
+ return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
+}
+
+static bool isDriveLetter(wchar_t c) {
+ return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
+}
+
+bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16) {
+ if (!UTF8ToWide(utf8, utf16)) {
+ return false;
+ }
+ // Note: Although most Win32 File I/O API are limited to MAX_PATH (260
+ // characters), the CreateDirectory API is limited to 248 characters.
+ if (utf16->length() >= 248) {
+ // If path is of the form "x:\" or "x:/"
+ if (isDriveLetter((*utf16)[0]) && (*utf16)[1] == L':' &&
+ ((*utf16)[2] == L'\\' || (*utf16)[2] == L'/')) {
+ // Append long path prefix, and make sure there are no unix-style
+ // separators to ensure a fully compliant Win32 long path string.
+ utf16->insert(0, LR"(\\?\)");
+ std::replace(utf16->begin(), utf16->end(), L'/', L'\\');
+ }
+ }
+ return true;
+}
+
+// Versions of standard library APIs that support UTF-8 strings.
+namespace utf8 {
+
+FILE* fopen(const char* name, const char* mode) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return nullptr;
+ }
+
+ std::wstring mode_utf16;
+ if (!UTF8ToWide(mode, &mode_utf16)) {
+ return nullptr;
+ }
+
+ return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
+}
+
+int mkdir(const char* name, mode_t) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return -1;
+ }
+
+ return _wmkdir(name_utf16.c_str());
+}
+
+int open(const char* name, int flags, ...) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return -1;
+ }
+
+ int mode = 0;
+ if ((flags & O_CREAT) != 0) {
+ va_list args;
+ va_start(args, flags);
+ mode = va_arg(args, int);
+ va_end(args);
+ }
+
+ return _wopen(name_utf16.c_str(), flags, mode);
+}
+
+int unlink(const char* name) {
+ std::wstring name_utf16;
+ if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
+ return -1;
+ }
+
+ return _wunlink(name_utf16.c_str());
+}
+
+} // namespace utf8
+} // namespace base
+} // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
new file mode 100644
index 000000000..472e82c63
--- /dev/null
+++ b/base/utf8_test.cpp
@@ -0,0 +1,488 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "android-base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace base {
+
+TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
+ std::wstring wide;
+
+ errno = 0;
+
+ // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
+ // error. Concatenate two C/C++ literal string constants to prevent the
+ // compiler from giving an error about "\xa2af" containing a "hex escape
+ // sequence out of range".
+ EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
+
+ EXPECT_EQ(EILSEQ, errno);
+
+ // Even if an invalid character is encountered, UTF8ToWide() should still do
+ // its best to convert the rest of the string. sysdeps_win32.cpp:
+ // _console_write_utf8() depends on this behavior.
+ //
+ // Thus, we verify that the valid characters are converted, but we ignore the
+ // specific replacement character that UTF8ToWide() may replace the invalid
+ // UTF-8 characters with because we want to allow that to change if the
+ // implementation changes.
+ EXPECT_EQ(0U, wide.find(L"before"));
+ const wchar_t after_wide[] = L"after";
+ EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
+
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The tests below from utf_string_conversions_unittest.cc check for this
+// preprocessor symbol, so define it, as it is appropriate for Windows.
+#define WCHAR_T_IS_UTF16
+static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
+
+// The tests below from utf_string_conversions_unittest.cc call versions of
+// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
+// stub implementations with that signature. These are just for testing and
+// should not be moved to base because they assert/expect no errors which is
+// probably not a good idea (or at least it is something that should be left
+// up to the caller, not a base library).
+
+static std::wstring UTF8ToWide(const std::string& utf8) {
+ std::wstring utf16;
+ EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
+ return utf16;
+}
+
+static std::string WideToUTF8(const std::wstring& utf16) {
+ std::string utf8;
+ EXPECT_TRUE(WideToUTF8(utf16, &utf8));
+ return utf8;
+}
+
+namespace {
+
+const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+} // namespace
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
+ // we round-trip all the wide strings through UTF-8 to make sure everything
+ // agrees on the conversion. This uses the stream operators to test them
+ // simultaneously.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::ostringstream utf8;
+ utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+ std::wostringstream wide;
+ wide << UTF8ToWide(utf8.str());
+
+ EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+ }
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
+ // An empty std::wstring should be converted to an empty std::string,
+ // and vice versa.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToUTF8(wempty));
+ EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
+ struct UTF8ToWideCase {
+ const char* utf8;
+ const wchar_t* wide;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-8 input.
+ {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
+ // Non-character is passed through.
+ {"\xef\xbf\xbfHello", L"\xffffHello", true},
+ // Truncated UTF-8 sequence.
+ {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // Truncated off the end.
+ {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
+ // Non-shortest-form UTF-8.
+ {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {"\xed\xb0\x80", L"\xfffd", false},
+ // Non-BMP characters. The second is a non-character regarded as valid.
+ // The result will either be in UTF-16 or UTF-32.
+#if defined(WCHAR_T_IS_UTF16)
+ {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
+#elif defined(WCHAR_T_IS_UTF32)
+ {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::wstring converted;
+ errno = 0;
+ const bool success = UTF8ToWide(convert_cases[i].utf8,
+ strlen(convert_cases[i].utf8),
+ &converted);
+ EXPECT_EQ(convert_cases[i].success, success);
+ // The original test always compared expected and converted, but don't do
+ // that because our implementation of UTF8ToWide() does not guarantee to
+ // produce the same output in error situations.
+ if (success) {
+ std::wstring expected(convert_cases[i].wide);
+ EXPECT_EQ(expected, converted);
+ } else {
+ EXPECT_EQ(EILSEQ, errno);
+ }
+ }
+
+ // Manually test an embedded NULL.
+ std::wstring converted;
+ EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
+ ASSERT_EQ(3U, converted.length());
+ EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
+ EXPECT_EQ('Z', converted[1]);
+ EXPECT_EQ('\t', converted[2]);
+
+ // Make sure that conversion replaces, not appends.
+ EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
+ ASSERT_EQ(1U, converted.length());
+ EXPECT_EQ('B', converted[0]);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// This test is only valid when wchar_t == UTF-16.
+TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf16;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-16 input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // The first character is a truncated UTF-16 character.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
+#if (WINVER >= 0x0600)
+ // Only Vista and later has a new API/flag that correctly returns false.
+ false
+#else
+ true
+#endif
+ },
+ // Truncated at the end.
+ // Note that for whatever reason, this test fails on Windows XP.
+ {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
+#if (WINVER >= 0x0600)
+ // Only Vista and later has a new API/flag that correctly returns false.
+ false
+#else
+ true
+#endif
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ errno = 0;
+ const bool success = WideToUTF8(convert_cases[i].utf16,
+ wcslen(convert_cases[i].utf16),
+ &converted);
+ EXPECT_EQ(convert_cases[i].success, success);
+ // The original test always compared expected and converted, but don't do
+ // that because our implementation of WideToUTF8() does not guarantee to
+ // produce the same output in error situations.
+ if (success) {
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ } else {
+ EXPECT_EQ(EILSEQ, errno);
+ }
+ }
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+// This test is only valid when wchar_t == UTF-32.
+TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf32;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular 16-bit input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // Invalid Unicode code points.
+ {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
+ // The first character is a truncated UTF-16 character.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+ {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
+ };
+
+ for (size_t i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ EXPECT_EQ(convert_cases[i].success,
+ WideToUTF8(convert_cases[i].utf32,
+ wcslen(convert_cases[i].utf32),
+ &converted));
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ }
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// The test below uses these types and functions, so just do enough to get the
+// test running.
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+template<typename T>
+static void* WriteInto(T* t, size_t size) {
+ // std::(w)string::resize() already includes space for a NULL terminator.
+ t->resize(size - 1);
+ return &(*t)[0];
+}
+
+// A stub implementation that calls a helper from above, just to get the test
+// below working. This is just for testing and should not be moved to base
+// because this ignores errors which is probably not a good idea, plus it takes
+// a string16 type which we don't really have.
+static std::string UTF16ToUTF8(const string16& utf16) {
+ return WideToUTF8(utf16);
+}
+
+TEST(UTFStringConversionsTest, ConvertMultiString) {
+ static char16 multi16[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ static char multi[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ string16 multistring16;
+ memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
+ sizeof(multi16));
+ EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
+ std::string expected;
+ memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+ EXPECT_EQ(arraysize(multi) - 1, expected.length());
+ const std::string& converted = UTF16ToUTF8(multistring16);
+ EXPECT_EQ(arraysize(multi) - 1, converted.length());
+ EXPECT_EQ(expected, converted);
+}
+
+// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
+// and SysUTF8ToWide(), so these are stub implementations that call the helpers
+// above. These are just for testing and should not be moved to base because
+// they ignore errors which is probably not a good idea.
+
+static std::string SysWideToUTF8(const std::wstring& utf16) {
+ return WideToUTF8(utf16);
+}
+
+static std::wstring SysUTF8ToWide(const std::string& utf8) {
+ return UTF8ToWide(utf8);
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef WCHAR_T_IS_UTF32
+static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
+#else
+static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
+#endif
+
+TEST(SysStrings, SysWideToUTF8) {
+ EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
+ EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
+
+ // >16 bits
+ EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
+
+ // Error case. When Windows finds a UTF-16 character going off the end of
+ // a string, it just converts that literal value to UTF-8, even though this
+ // is invalid.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+ // SysWideToUTF8(L"\x4f60\xd800zyxw"));
+
+ // Test embedded NULLs.
+ std::wstring wide_null(L"a");
+ wide_null.push_back(0);
+ wide_null.push_back('b');
+
+ std::string expected_null("a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
+}
+
+TEST(SysStrings, SysUTF8ToWide) {
+ EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
+ EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+ // >16 bits
+ EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
+
+ // Error case. When Windows finds an invalid UTF-8 character, it just skips
+ // it. This seems weird because it's inconsistent with the reverse conversion.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+ // Test embedded NULLs.
+ std::string utf8_null("a");
+ utf8_null.push_back(0);
+ utf8_null.push_back('b');
+
+ std::wstring expected_null(L"a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, DontAddPrefixIfShorterThanMaxPath) {
+ std::string utf8 = "c:\\mypath\\myfile.txt";
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(std::string::npos, wide.find(LR"(\\?\)"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixIfLongerThanMaxPath) {
+ std::string utf8 = "c:\\mypath";
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "\\mypathsegment";
+ }
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+ EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+TEST(UTF8PathToWindowsLongPathTest, AddPrefixAndFixSeparatorsIfLongerThanMaxPath) {
+ std::string utf8 = "c:/mypath";
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "/mypathsegment";
+ }
+
+ std::wstring wide;
+ EXPECT_TRUE(UTF8PathToWindowsLongPath(utf8.c_str(), &wide));
+
+ EXPECT_EQ(0U, wide.find(LR"(\\?\)"));
+ EXPECT_EQ(std::string::npos, wide.find(L"/"));
+}
+
+namespace utf8 {
+
+TEST(Utf8FilesTest, CanCreateOpenAndDeleteFileWithLongPath) {
+ TemporaryDir td;
+
+ // Create long directory path
+ std::string utf8 = td.path;
+ while (utf8.length() < 300 /* MAX_PATH is 260 */) {
+ utf8 += "\\mypathsegment";
+ EXPECT_EQ(0, mkdir(utf8.c_str(), 0));
+ }
+
+ // Create file
+ utf8 += "\\test-file.bin";
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
+ int mode = 0666;
+ android::base::unique_fd fd(open(utf8.c_str(), flags, mode));
+ EXPECT_NE(-1, fd.get());
+
+ // Close file
+ fd.reset();
+ EXPECT_EQ(-1, fd.get());
+
+ // Open file with fopen
+ FILE* file = fopen(utf8.c_str(), "rb");
+ EXPECT_NE(nullptr, file);
+
+ if (file) {
+ fclose(file);
+ }
+
+ // Delete file
+ EXPECT_EQ(0, unlink(utf8.c_str()));
+}
+
+} // namespace utf8
+} // namespace base
+} // namespace android
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index ca59ef39b..edff26df5 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
bootstat_lib_src_files = [
"boot_event_record_store.cpp",
]
@@ -35,7 +31,7 @@ cc_defaults {
"libcutils",
"liblog",
],
- header_libs: ["libgtest_prod_headers"],
+ static_libs: ["libgtest_prod"],
}
// bootstat static library
@@ -97,7 +93,4 @@ cc_test {
"boot_event_record_store_test.cpp",
"testrunner.cpp",
],
- test_options: {
- unit_test: true,
- },
}
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 000000000..f3783fa2d
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for bootstat_tests">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="bootstat_tests" />
+ </test>
+</configuration> \ No newline at end of file
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
index f66b309bb..50b209717 100644
--- a/bootstat/OWNERS
+++ b/bootstat/OWNERS
@@ -1,2 +1,2 @@
jhawkins@google.com
-dvander@google.com
+salyzyn@google.com
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 7cff7dc9f..2f2919f2a 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -1331,7 +1331,7 @@ if [ X"--macros" != X"${1}" ]; then
shift
fi
- # Check if all conditions for the script are valid
+ # Check if all conditions for the script are sane
if [ -z "${ANDROID_SERIAL}" ]; then
ndev=`(
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d6ebb0d54..5d6cee43c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -436,9 +436,6 @@ const std::map<std::string, int32_t> kBootReasonMap = {
{"reboot,userspace_failed,watchdog_fork", 188},
{"reboot,userspace_failed,*", 189},
{"reboot,mount_userdata_failed", 190},
- {"reboot,forcedsilent", 191},
- {"reboot,forcednonsilent", 192},
- {"reboot,thermal,tj", 193},
};
// Converts a string value representing the reason the system booted to an
@@ -1323,8 +1320,6 @@ void RecordBootComplete() {
// Record the total time from device startup to boot complete, regardless of
// encryption state.
- // Note: we are recording seconds here even though the field in statsd atom specifies
- // milliseconds.
boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
diff --git a/cli-test/Android.bp b/cli-test/Android.bp
index 59618b473..37a1d1b37 100644
--- a/cli-test/Android.bp
+++ b/cli-test/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "cli-test",
host_supported: true,
diff --git a/cli-test/cli-test.cpp b/cli-test/cli-test.cpp
index d1ef1b431..d6e27ee3b 100644
--- a/cli-test/cli-test.cpp
+++ b/cli-test/cli-test.cpp
@@ -146,13 +146,6 @@ static void CollectTests(std::vector<Test>* tests, const char* test_filename) {
test->befores.push_back(line);
} else if (Match(&line, "after:")) {
test->afters.push_back(line);
- } else if (Match(&line, "expected-exit-status:")) {
- char* end_p;
- errno = 0;
- test->exit_status = strtol(line.c_str(), &end_p, 10);
- if (errno != 0 || *end_p != '\0') {
- Die(0, "%s:%zu: bad exit status: \"%s\"", g_file, g_line, line.c_str());
- }
} else if (Match(&line, "expected-stdout:")) {
// Collect tab-indented lines.
std::string text;
@@ -238,15 +231,15 @@ static int RunTests(const std::vector<Test>& tests) {
V("running command \"%s\"", test.command.c_str());
CapturedStdout test_stdout;
CapturedStderr test_stderr;
- int status = system(test.command.c_str());
+ int exit_status = system(test.command.c_str());
test_stdout.Stop();
test_stderr.Stop();
- V("system() returned status %d", status);
- if (WEXITSTATUS(status) != test.exit_status) {
+ V("exit status %d", exit_status);
+ if (exit_status != test.exit_status) {
failed = true;
fprintf(stderr, "Incorrect exit status: expected %d but %s\n", test.exit_status,
- ExitStatusToString(status).c_str());
+ ExitStatusToString(exit_status).c_str());
}
if (!CheckOutput("stdout", test_stdout.str(), test.expected_stdout, FILES)) failed = true;
diff --git a/code_coverage/Android.bp b/code_coverage/Android.bp
index 2cb1617ef..b51c80288 100644
--- a/code_coverage/Android.bp
+++ b/code_coverage/Android.bp
@@ -1,8 +1,4 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
prebuilt_etc {
name: "code_coverage.policy",
sub_dir: "seccomp_policy",
diff --git a/code_coverage/seccomp_policy/code_coverage.arm.policy b/code_coverage/seccomp_policy/code_coverage.arm.policy
index 35893798e..b80910f19 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm.policy
@@ -1,5 +1,4 @@
close: 1
-fchmod: 1
mkdirat: 1
msync: 1
munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.arm64.policy b/code_coverage/seccomp_policy/code_coverage.arm64.policy
index fdb4d1e54..7040ea258 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm64.policy
@@ -1,5 +1,4 @@
close: 1
-fchmod: 1
mkdirat: 1
msync: 1
munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.policy.def b/code_coverage/seccomp_policy/code_coverage.policy.def
index b6a4c6d2c..599c4a458 100644
--- a/code_coverage/seccomp_policy/code_coverage.policy.def
+++ b/code_coverage/seccomp_policy/code_coverage.policy.def
@@ -13,9 +13,6 @@
// 2nd-Nth: uses mmap() to update in place
close: 1
-// fchmod allowed to set libprofile-clang-extras, which wraps `open` calls, to
-// set correct permission for coverage files.
-fchmod: 1
mkdirat: 1
msync: 1
munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86.policy b/code_coverage/seccomp_policy/code_coverage.x86.policy
index 145d3a36c..f8e0cc0f4 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86.policy
@@ -1,5 +1,4 @@
close: 1
-fchmod: 1
mkdirat: 1
msync: 1
munmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86_64.policy b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
index 11c80753c..dcf2f9a11 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86_64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
@@ -1,5 +1,4 @@
close: 1
-fchmod: 1
mkdirat: 1
msync: 1
munmap: 1
diff --git a/cpio/Android.bp b/cpio/Android.bp
new file mode 100644
index 000000000..baa031907
--- /dev/null
+++ b/cpio/Android.bp
@@ -0,0 +1,11 @@
+// Copyright 2005 The Android Open Source Project
+
+cc_binary_host {
+ name: "mkbootfs",
+ srcs: ["mkbootfs.c"],
+ cflags: ["-Werror"],
+ shared_libs: ["libcutils"],
+ dist: {
+ targets: ["dist_files"],
+ },
+}
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
new file mode 100644
index 000000000..e52762e9b
--- /dev/null
+++ b/cpio/mkbootfs.c
@@ -0,0 +1,367 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include <private/android_filesystem_config.h>
+
+/* NOTES
+**
+** - see buffer-format.txt from the linux kernel docs for
+** an explanation of this file format
+** - dotfiles are ignored
+** - directories named 'root' are ignored
+** - device notes, pipes, etc are not supported (error)
+*/
+
+void die(const char *why, ...)
+{
+ va_list ap;
+
+ va_start(ap, why);
+ fprintf(stderr,"error: ");
+ vfprintf(stderr, why, ap);
+ fprintf(stderr,"\n");
+ va_end(ap);
+ exit(1);
+}
+
+struct fs_config_entry {
+ char* name;
+ int uid, gid, mode;
+};
+
+static struct fs_config_entry* canned_config = NULL;
+static char *target_out_path = NULL;
+
+/* Each line in the canned file should be a path plus three ints (uid,
+ * gid, mode). */
+#ifdef PATH_MAX
+#define CANNED_LINE_LENGTH (PATH_MAX+100)
+#else
+#define CANNED_LINE_LENGTH (1024)
+#endif
+
+#define TRAILER "TRAILER!!!"
+
+static int verbose = 0;
+static int total_size = 0;
+
+static void fix_stat(const char *path, struct stat *s)
+{
+ uint64_t capabilities;
+ if (canned_config) {
+ // Use the list of file uid/gid/modes loaded from the file
+ // given with -f.
+
+ struct fs_config_entry* empty_path_config = NULL;
+ struct fs_config_entry* p;
+ for (p = canned_config; p->name; ++p) {
+ if (!p->name[0]) {
+ empty_path_config = p;
+ }
+ if (strcmp(p->name, path) == 0) {
+ s->st_uid = p->uid;
+ s->st_gid = p->gid;
+ s->st_mode = p->mode | (s->st_mode & ~07777);
+ return;
+ }
+ }
+ s->st_uid = empty_path_config->uid;
+ s->st_gid = empty_path_config->gid;
+ s->st_mode = empty_path_config->mode | (s->st_mode & ~07777);
+ } else {
+ // Use the compiled-in fs_config() function.
+ unsigned st_mode = s->st_mode;
+ int is_dir = S_ISDIR(s->st_mode) || strcmp(path, TRAILER) == 0;
+ fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities);
+ s->st_mode = (typeof(s->st_mode)) st_mode;
+ }
+}
+
+static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
+{
+ // Nothing is special about this value, just picked something in the
+ // approximate range that was being used already, and avoiding small
+ // values which may be special.
+ static unsigned next_inode = 300000;
+
+ while(total_size & 3) {
+ total_size++;
+ putchar(0);
+ }
+
+ fix_stat(out, s);
+// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
+
+ printf("%06x%08x%08x%08x%08x%08x%08x"
+ "%08x%08x%08x%08x%08x%08x%08x%s%c",
+ 0x070701,
+ next_inode++, // s.st_ino,
+ s->st_mode,
+ 0, // s.st_uid,
+ 0, // s.st_gid,
+ 1, // s.st_nlink,
+ 0, // s.st_mtime,
+ datasize,
+ 0, // volmajor
+ 0, // volminor
+ 0, // devmajor
+ 0, // devminor,
+ olen + 1,
+ 0,
+ out,
+ 0
+ );
+
+ total_size += 6 + 8*13 + olen + 1;
+
+ if(strlen(out) != (unsigned int)olen) die("ACK!");
+
+ while(total_size & 3) {
+ total_size++;
+ putchar(0);
+ }
+
+ if(datasize) {
+ fwrite(data, datasize, 1, stdout);
+ total_size += datasize;
+ }
+}
+
+static void _eject_trailer()
+{
+ struct stat s;
+ memset(&s, 0, sizeof(s));
+ _eject(&s, TRAILER, 10, 0, 0);
+
+ while(total_size & 0xff) {
+ total_size++;
+ putchar(0);
+ }
+}
+
+static void _archive(char *in, char *out, int ilen, int olen);
+
+static int compare(const void* a, const void* b) {
+ return strcmp(*(const char**)a, *(const char**)b);
+}
+
+static void _archive_dir(char *in, char *out, int ilen, int olen)
+{
+ int i, t;
+ DIR *d;
+ struct dirent *de;
+
+ if(verbose) {
+ fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
+ in, out, ilen, olen);
+ }
+
+ d = opendir(in);
+ if(d == 0) die("cannot open directory '%s'", in);
+
+ int size = 32;
+ int entries = 0;
+ char** names = malloc(size * sizeof(char*));
+ if (names == NULL) {
+ fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
+ exit(1);
+ }
+
+ while((de = readdir(d)) != 0){
+ /* xxx: feature? maybe some dotfiles are okay */
+ if(de->d_name[0] == '.') continue;
+
+ /* xxx: hack. use a real exclude list */
+ if(!strcmp(de->d_name, "root")) continue;
+
+ if (entries >= size) {
+ size *= 2;
+ names = realloc(names, size * sizeof(char*));
+ if (names == NULL) {
+ fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
+ size);
+ exit(1);
+ }
+ }
+ names[entries] = strdup(de->d_name);
+ if (names[entries] == NULL) {
+ fprintf(stderr, "failed to strdup name \"%s\"\n",
+ de->d_name);
+ exit(1);
+ }
+ ++entries;
+ }
+
+ qsort(names, entries, sizeof(char*), compare);
+
+ for (i = 0; i < entries; ++i) {
+ t = strlen(names[i]);
+ in[ilen] = '/';
+ memcpy(in + ilen + 1, names[i], t + 1);
+
+ if(olen > 0) {
+ out[olen] = '/';
+ memcpy(out + olen + 1, names[i], t + 1);
+ _archive(in, out, ilen + t + 1, olen + t + 1);
+ } else {
+ memcpy(out, names[i], t + 1);
+ _archive(in, out, ilen + t + 1, t);
+ }
+
+ in[ilen] = 0;
+ out[olen] = 0;
+
+ free(names[i]);
+ }
+ free(names);
+
+ closedir(d);
+}
+
+static void _archive(char *in, char *out, int ilen, int olen)
+{
+ struct stat s;
+
+ if(verbose) {
+ fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
+ in, out, ilen, olen);
+ }
+
+ if(lstat(in, &s)) die("could not stat '%s'\n", in);
+
+ if(S_ISREG(s.st_mode)){
+ char *tmp;
+ int fd;
+
+ fd = open(in, O_RDONLY);
+ if(fd < 0) die("cannot open '%s' for read", in);
+
+ tmp = (char*) malloc(s.st_size);
+ if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
+
+ if(read(fd, tmp, s.st_size) != s.st_size) {
+ die("cannot read %d bytes", s.st_size);
+ }
+
+ _eject(&s, out, olen, tmp, s.st_size);
+
+ free(tmp);
+ close(fd);
+ } else if(S_ISDIR(s.st_mode)) {
+ _eject(&s, out, olen, 0, 0);
+ _archive_dir(in, out, ilen, olen);
+ } else if(S_ISLNK(s.st_mode)) {
+ char buf[1024];
+ int size;
+ size = readlink(in, buf, 1024);
+ if(size < 0) die("cannot read symlink '%s'", in);
+ _eject(&s, out, olen, buf, size);
+ } else {
+ die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
+ }
+}
+
+void archive(const char *start, const char *prefix)
+{
+ char in[8192];
+ char out[8192];
+
+ strcpy(in, start);
+ strcpy(out, prefix);
+
+ _archive_dir(in, out, strlen(in), strlen(out));
+}
+
+static void read_canned_config(char* filename)
+{
+ int allocated = 8;
+ int used = 0;
+
+ canned_config =
+ (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
+
+ char line[CANNED_LINE_LENGTH];
+ FILE* f = fopen(filename, "r");
+ if (f == NULL) die("failed to open canned file");
+
+ while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
+ if (!line[0]) break;
+ if (used >= allocated) {
+ allocated *= 2;
+ canned_config = (struct fs_config_entry*)realloc(
+ canned_config, allocated * sizeof(struct fs_config_entry));
+ if (canned_config == NULL) die("failed to reallocate memory");
+ }
+
+ struct fs_config_entry* cc = canned_config + used;
+
+ if (isspace(line[0])) {
+ cc->name = strdup("");
+ cc->uid = atoi(strtok(line, " \n"));
+ } else {
+ cc->name = strdup(strtok(line, " \n"));
+ cc->uid = atoi(strtok(NULL, " \n"));
+ }
+ cc->gid = atoi(strtok(NULL, " \n"));
+ cc->mode = strtol(strtok(NULL, " \n"), NULL, 8);
+ ++used;
+ }
+ if (used >= allocated) {
+ ++allocated;
+ canned_config = (struct fs_config_entry*)realloc(
+ canned_config, allocated * sizeof(struct fs_config_entry));
+ if (canned_config == NULL) die("failed to reallocate memory");
+ }
+ canned_config[used].name = NULL;
+
+ fclose(f);
+}
+
+
+int main(int argc, char *argv[])
+{
+ argc--;
+ argv++;
+
+ if (argc > 1 && strcmp(argv[0], "-d") == 0) {
+ target_out_path = argv[1];
+ argc -= 2;
+ argv += 2;
+ }
+
+ if (argc > 1 && strcmp(argv[0], "-f") == 0) {
+ read_canned_config(argv[1]);
+ argc -= 2;
+ argv += 2;
+ }
+
+ if(argc == 0) die("no directories to process?!");
+
+ while(argc-- > 0){
+ char *x = strchr(*argv, '=');
+ if(x != 0) {
+ *x++ = 0;
+ } else {
+ x = "";
+ }
+
+ archive(*argv, x);
+
+ argv++;
+ }
+
+ _eject_trailer();
+
+ return 0;
+}
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 198e4defb..c7bd1a83b 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,31 +1,3 @@
-package {
- default_applicable_licenses: ["system_core_debuggerd_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "system_core_debuggerd_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-BSD",
- ],
- // large-scale-change unable to identify any license_text files
-}
-
cc_defaults {
name: "debuggerd_defaults",
cflags: [
@@ -45,7 +17,6 @@ cc_library_headers {
name: "libdebuggerd_common_headers",
export_include_dirs: ["common/include"],
recovery_available: true,
- vendor_ramdisk_available: true,
}
cc_library_shared {
@@ -76,7 +47,6 @@ cc_library_static {
name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
recovery_available: true,
- vendor_ramdisk_available: true,
srcs: [
"tombstoned/tombstoned_client.cpp",
"util.cpp",
@@ -99,7 +69,6 @@ cc_library_static {
name: "libdebuggerd_handler_core",
defaults: ["debuggerd_defaults"],
recovery_available: true,
- vendor_ramdisk_available: true,
srcs: ["handler/debuggerd_handler.cpp"],
header_libs: [
@@ -144,7 +113,6 @@ cc_library_static {
],
defaults: ["debuggerd_defaults"],
recovery_available: true,
- vendor_ramdisk_available: true,
srcs: [
"handler/debuggerd_fallback.cpp",
],
@@ -196,29 +164,20 @@ cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
recovery_available: true,
- vendor_ramdisk_available: true,
srcs: [
"libdebuggerd/backtrace.cpp",
"libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
- "libdebuggerd/scudo.cpp",
"libdebuggerd/tombstone.cpp",
- "libdebuggerd/tombstone_proto.cpp",
- "libdebuggerd/tombstone_proto_to_text.cpp",
"libdebuggerd/utility.cpp",
],
local_include_dirs: ["libdebuggerd/include"],
export_include_dirs: ["libdebuggerd/include"],
- include_dirs: [
- // Needed for private/bionic_fdsan.h
- "bionic/libc",
-
- // Needed for scudo/interface.h
- "external/scudo/standalone/include",
- ],
+ // Needed for private/bionic_fdsan.h
+ include_dirs: ["bionic/libc"],
header_libs: [
"bionic_libc_platform_headers",
"gwp_asan_headers",
@@ -233,14 +192,7 @@ cc_library_static {
"liblog",
],
- whole_static_libs: [
- "libasync_safe",
- "gwp_asan_crash_handler",
- "libscudo",
- "libtombstone_proto",
- "libprocinfo",
- "libprotobuf-cpp-lite",
- ],
+ whole_static_libs: ["gwp_asan_crash_handler"],
target: {
recovery: {
@@ -248,11 +200,6 @@ cc_library_static {
"libdexfile_support",
],
},
- vendor_ramdisk: {
- exclude_static_libs: [
- "libdexfile_support",
- ],
- },
},
product_variables: {
@@ -262,26 +209,6 @@ cc_library_static {
},
}
-cc_binary {
- name: "pbtombstone",
- defaults: ["debuggerd_defaults"],
- srcs: ["pbtombstone.cpp"],
- static_libs: [
- "libbase",
- "libdebuggerd",
- "liblog",
- "libprotobuf-cpp-lite",
- "libtombstone_proto",
- "libunwindstack",
- ],
-}
-
-cc_test_library {
- name: "libcrash_test",
- defaults: ["debuggerd_defaults"],
- srcs: ["crash_test.cpp"],
-}
-
cc_test {
name: "debuggerd_test",
defaults: ["debuggerd_defaults"],
@@ -314,6 +241,7 @@ cc_test {
"libcutils",
"libdebuggerd_client",
"liblog",
+ "libminijail",
"libnativehelper",
"libunwindstack",
],
@@ -321,7 +249,6 @@ cc_test {
static_libs: [
"libdebuggerd",
"libgmock",
- "libminijail",
],
header_libs: [
@@ -329,10 +256,6 @@ cc_test {
"gwp_asan_headers",
],
- include_dirs: [
- "external/scudo/standalone/include",
- ],
-
local_include_dirs: [
"libdebuggerd",
],
@@ -347,10 +270,6 @@ cc_test {
},
},
- data: [
- ":libcrash_test",
- ],
-
test_suites: ["device-tests"],
}
@@ -390,9 +309,6 @@ cc_binary {
"libtombstoned_client_static",
"libdebuggerd",
"libcutils",
-
- "libtombstone_proto",
- "libprotobuf-cpp-lite",
],
shared_libs: [
@@ -401,10 +317,6 @@ cc_binary {
"libprocinfo",
"libunwindstack",
],
-
- apex_available: [
- "com.android.runtime",
- ],
}
cc_binary {
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 530e0e88f..5c027387c 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -70,6 +70,36 @@ static void populate_timeval(struct timeval* tv, const Duration& duration) {
tv->tv_usec = static_cast<long>(microseconds.count());
}
+static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
+ struct tm now;
+ time_t t = time(nullptr);
+ localtime_r(&t, &now);
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
+ std::string time_now(timestamp);
+
+ std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
+
+ char proc_name_buf[1024];
+ const char* proc_name = nullptr;
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
+
+ if (fp) {
+ proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
+ }
+
+ if (!proc_name) {
+ proc_name = "<unknown>";
+ }
+
+ buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
+ << "Cmd line: " << proc_name << "\n";
+}
+
+static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
+ buffer << "----- end " << std::to_string(pid) << " -----\n";
+}
+
/**
* Returns the wchan data for each thread in the process,
* or empty string if unable to obtain any data.
@@ -95,10 +125,9 @@ static std::string get_wchan_data(pid_t pid) {
}
if (std::string str = data.str(); !str.empty()) {
- buffer << "\n----- Waiting Channels: pid " << pid << " at " << get_timestamp() << " -----\n"
- << "Cmd line: " << android::base::Join(get_command_line(pid), " ") << "\n";
+ get_wchan_header(pid, buffer);
buffer << "\n" << str << "\n";
- buffer << "----- end " << std::to_string(pid) << " -----\n";
+ get_wchan_footer(pid, buffer);
buffer << "\n";
}
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
index a3e171b25..203269e96 100644
--- a/debuggerd/common/include/dump_type.h
+++ b/debuggerd/common/include/dump_type.h
@@ -24,8 +24,7 @@ enum DebuggerdDumpType : uint8_t {
kDebuggerdNativeBacktrace,
kDebuggerdTombstone,
kDebuggerdJavaBacktrace,
- kDebuggerdAnyIntercept,
- kDebuggerdTombstoneProto,
+ kDebuggerdAnyIntercept
};
inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
@@ -42,9 +41,6 @@ inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& r
case kDebuggerdAnyIntercept:
stream << "kDebuggerdAnyIntercept";
break;
- case kDebuggerdTombstoneProto:
- stream << "kDebuggerdTombstoneProto";
- break;
default:
stream << "[unknown]";
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index a15274019..3e998802b 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -40,7 +40,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <bionic/macros.h>
#include <bionic/reserved_signals.h>
#include <cutils/sockets.h>
#include <log/log.h>
@@ -153,14 +152,14 @@ static bool activity_manager_notify(pid_t pid, int signal, const std::string& am
}
struct timeval tv = {
- .tv_sec = 1 * android::base::HwTimeoutMultiplier(),
- .tv_usec = 0,
+ .tv_sec = 1,
+ .tv_usec = 0,
};
if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
PLOG(ERROR) << "failed to set send timeout on activity manager socket";
return false;
}
- tv.tv_sec = 3 * android::base::HwTimeoutMultiplier(); // 3 seconds on handshake read
+ tv.tv_sec = 3; // 3 seconds on handshake read
if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
PLOG(ERROR) << "failed to set receive timeout on activity manager socket";
return false;
@@ -195,7 +194,6 @@ static pid_t g_target_thread = -1;
static bool g_tombstoned_connected = false;
static unique_fd g_tombstoned_socket;
static unique_fd g_output_fd;
-static unique_fd g_proto_fd;
static void DefuseSignalHandlers() {
// Don't try to dump ourselves.
@@ -216,7 +214,7 @@ static void Initialize(char** argv) {
// If we abort before we get an output fd, contact tombstoned to let any
// potential listeners know that we failed.
if (!g_tombstoned_connected) {
- if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, &g_proto_fd,
+ if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd,
kDebuggerdAnyIntercept)) {
// We failed to connect, not much we can do.
LOG(ERROR) << "failed to connected to tombstoned to report failure";
@@ -249,24 +247,16 @@ static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdD
}
int dump_type_int;
- if (!android::base::ParseInt(argv[3], &dump_type_int, 0)) {
+ if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) {
LOG(FATAL) << "invalid requested dump type: " << argv[3];
}
-
*dump_type = static_cast<DebuggerdDumpType>(dump_type_int);
- switch (*dump_type) {
- case kDebuggerdNativeBacktrace:
- case kDebuggerdTombstone:
- case kDebuggerdTombstoneProto:
- break;
-
- default:
- LOG(FATAL) << "invalid requested dump type: " << dump_type_int;
- }
}
static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
- std::unique_ptr<unwindstack::Regs>* regs, ProcessInfo* process_info) {
+ std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
+ uintptr_t* fdsan_table_address, uintptr_t* gwp_asan_state,
+ uintptr_t* gwp_asan_metadata) {
std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
@@ -276,13 +266,15 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
ssize_t expected_size = 0;
switch (crash_info->header.version) {
case 1:
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV1);
+ break;
+
case 2:
- case 3:
- expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
break;
- case 4:
- expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
+ case 3:
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
break;
default:
@@ -290,34 +282,28 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
break;
};
- if (rc < expected_size) {
+ if (rc != expected_size) {
LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
<< expected_size;
}
}
+ *fdsan_table_address = 0;
+ *gwp_asan_state = 0;
+ *gwp_asan_metadata = 0;
switch (crash_info->header.version) {
- case 4:
- process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address;
- process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
- process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
- process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
- process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
- process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer;
+ case 3:
+ *gwp_asan_state = crash_info->data.v3.gwp_asan_state;
+ *gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata;
FALLTHROUGH_INTENDED;
- case 1:
case 2:
- case 3:
- process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
- *siginfo = crash_info->data.s.siginfo;
- if (signal_has_si_addr(siginfo)) {
- process_info->has_fault_address = true;
- process_info->maybe_tagged_fault_address = reinterpret_cast<uintptr_t>(siginfo->si_addr);
- process_info->untagged_fault_address =
- untag_address(reinterpret_cast<uintptr_t>(siginfo->si_addr));
- }
+ *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+ FALLTHROUGH_INTENDED;
+ case 1:
+ *abort_msg_address = crash_info->data.v1.abort_msg_address;
+ *siginfo = crash_info->data.v1.siginfo;
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
- &crash_info->data.s.ucontext));
+ &crash_info->data.v1.ucontext));
break;
default:
@@ -391,7 +377,7 @@ int main(int argc, char** argv) {
// There appears to be a bug in the kernel where our death causes SIGHUP to
// be sent to our process group if we exit while it has stopped jobs (e.g.
- // because of wait_for_debugger). Use setsid to create a new process group to
+ // because of wait_for_gdb). Use setsid to create a new process group to
// avoid hitting this.
setsid();
@@ -439,7 +425,10 @@ int main(int argc, char** argv) {
ATRACE_NAME("after reparent");
pid_t pseudothread_tid;
DebuggerdDumpType dump_type;
- ProcessInfo process_info;
+ uintptr_t abort_msg_address = 0;
+ uintptr_t fdsan_table_address = 0;
+ uintptr_t gwp_asan_state = 0;
+ uintptr_t gwp_asan_metadata = 0;
Initialize(argv);
ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -448,7 +437,10 @@ int main(int argc, char** argv) {
//
// Note: processes with many threads and minidebug-info can take a bit to
// unwind, do not make this too small. b/62828735
- alarm(30 * android::base::HwTimeoutMultiplier());
+ alarm(30);
+
+ // Get the process name (aka cmdline).
+ std::string process_name = get_process_name(g_target_thread);
// Collect the list of open files.
OpenFilesList open_files;
@@ -486,35 +478,21 @@ int main(int argc, char** argv) {
info.pid = target_process;
info.tid = thread;
info.uid = getuid();
+ info.process_name = process_name;
info.thread_name = get_thread_name(thread);
- unique_fd attr_fd(openat(target_proc_fd, "attr/current", O_RDONLY | O_CLOEXEC));
- if (!android::base::ReadFdToString(attr_fd, &info.selinux_label)) {
- PLOG(WARNING) << "failed to read selinux label";
- }
-
if (!ptrace_interrupt(thread, &info.signo)) {
PLOG(WARNING) << "failed to ptrace interrupt thread " << thread;
ptrace(PTRACE_DETACH, thread, 0, 0);
continue;
}
- struct iovec iov = {
- &info.tagged_addr_ctrl,
- sizeof(info.tagged_addr_ctrl),
- };
- if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TAGGED_ADDR_CTRL,
- reinterpret_cast<void*>(&iov)) == -1) {
- info.tagged_addr_ctrl = -1;
- }
-
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
- ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info);
+ ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
+ &fdsan_table_address, &gwp_asan_state, &gwp_asan_metadata);
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
-
- info.command_line = get_command_line(g_target_thread);
} else {
info.registers.reset(unwindstack::Regs::RemoteGet(thread));
if (!info.registers) {
@@ -546,17 +524,15 @@ int main(int argc, char** argv) {
fork_exit_write.reset();
// Defer the message until later, for readability.
- bool wait_for_debugger = android::base::GetBoolProperty(
- "debug.debuggerd.wait_for_debugger",
- android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false));
+ bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
if (siginfo.si_signo == BIONIC_SIGNAL_DEBUGGER) {
- wait_for_debugger = false;
+ wait_for_gdb = false;
}
// Detach from all of our attached threads before resuming.
for (const auto& [tid, thread] : thread_info) {
int resume_signal = thread.signo == BIONIC_SIGNAL_DEBUGGER ? 0 : thread.signo;
- if (wait_for_debugger) {
+ if (wait_for_gdb) {
resume_signal = 0;
if (tgkill(target_process, tid, SIGSTOP) != 0) {
PLOG(WARNING) << "failed to send SIGSTOP to " << tid;
@@ -575,8 +551,8 @@ int main(int argc, char** argv) {
{
ATRACE_NAME("tombstoned_connect");
LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type;
- g_tombstoned_connected = tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd,
- &g_proto_fd, dump_type);
+ g_tombstoned_connected =
+ tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type);
}
if (g_tombstoned_connected) {
@@ -611,8 +587,8 @@ int main(int argc, char** argv) {
}
// TODO: Use seccomp to lock ourselves down.
- unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
- if (!unwinder.Init()) {
+ unwindstack::UnwinderFromPid unwinder(256, vm_pid);
+ if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
LOG(FATAL) << "Failed to init unwinder object.";
}
@@ -623,14 +599,14 @@ int main(int argc, char** argv) {
} else {
{
ATRACE_NAME("fdsan table dump");
- populate_fdsan_table(&open_files, unwinder.GetProcessMemory(),
- process_info.fdsan_table_address);
+ populate_fdsan_table(&open_files, unwinder.GetProcessMemory(), fdsan_table_address);
}
{
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
- g_target_thread, process_info, &open_files, &amfd_data);
+ engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
+ abort_msg_address, &open_files, &amfd_data, gwp_asan_state,
+ gwp_asan_metadata);
}
}
@@ -641,12 +617,12 @@ int main(int argc, char** argv) {
}
}
- if (wait_for_debugger) {
+ if (wait_for_gdb) {
// Use ALOGI to line up with output from engrave_tombstone.
ALOGI(
"***********************************************************\n"
"* Process %d has been suspended while crashing.\n"
- "* To attach the debugger, run this on the host:\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
"*\n"
"* gdbclient.py -p %d\n"
"*\n"
diff --git a/debuggerd/crash_test.cpp b/debuggerd/crash_test.cpp
deleted file mode 100644
index c15145f61..000000000
--- a/debuggerd/crash_test.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-
-extern "C" void crash() {
- *reinterpret_cast<volatile char*>(0xdead) = '1';
-}
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 23b106ea4..e86f499bd 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "crasher-defaults",
@@ -13,6 +9,7 @@ cc_defaults {
"-Werror",
"-O0",
"-fstack-protector-all",
+ "-Wno-free-nonheap-object",
"-Wno-date-time",
],
srcs: ["crasher.cpp"],
@@ -27,6 +24,12 @@ cc_defaults {
arm64: {
srcs: ["arm64/crashglue.S"],
},
+ mips: {
+ srcs: ["mips/crashglue.S"],
+ },
+ mips64: {
+ srcs: ["mips64/crashglue.S"],
+ },
x86: {
srcs: ["x86/crashglue.S"],
},
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index db30b8f07..304166472 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -134,14 +134,10 @@ noinline int crash(int a) {
return a*2;
}
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wfree-nonheap-object"
-
noinline void abuse_heap() {
char buf[16];
free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
}
-#pragma clang diagnostic pop
noinline void leak() {
while (true) {
@@ -353,7 +349,7 @@ noinline int do_action(const char* arg) {
int main(int argc, char** argv) {
#if defined(STATIC_CRASHER)
debuggerd_callbacks_t callbacks = {
- .get_process_info = []() {
+ .get_abort_message = []() {
static struct {
size_t size;
char msg[32];
@@ -361,9 +357,7 @@ int main(int argc, char** argv) {
msg.size = strlen("dummy abort message");
memcpy(msg.msg, "dummy abort message", strlen("dummy abort message"));
- return debugger_process_info{
- .abort_msg = reinterpret_cast<void*>(&msg),
- };
+ return reinterpret_cast<abort_msg_t*>(&msg);
},
.post_dump = nullptr
};
diff --git a/debuggerd/crasher/mips/crashglue.S b/debuggerd/crasher/mips/crashglue.S
new file mode 100644
index 000000000..70a664127
--- /dev/null
+++ b/debuggerd/crasher/mips/crashglue.S
@@ -0,0 +1,48 @@
+ .set noat
+
+ .globl crash1
+ .globl crashnostack
+
+crash1:
+ li $0,0xdead0000+0
+ li $1,0xdead0000+1
+ li $2,0xdead0000+2
+ li $3,0xdead0000+3
+ li $4,0xdead0000+4
+ li $5,0xdead0000+5
+ li $6,0xdead0000+6
+ li $7,0xdead0000+7
+ li $8,0xdead0000+8
+ li $9,0xdead0000+9
+ li $10,0xdead0000+10
+ li $11,0xdead0000+11
+ li $12,0xdead0000+12
+ li $13,0xdead0000+13
+ li $14,0xdead0000+14
+ li $15,0xdead0000+15
+ li $16,0xdead0000+16
+ li $17,0xdead0000+17
+ li $18,0xdead0000+18
+ li $19,0xdead0000+19
+ li $20,0xdead0000+20
+ li $21,0xdead0000+21
+ li $22,0xdead0000+22
+ li $23,0xdead0000+23
+ li $24,0xdead0000+24
+ li $25,0xdead0000+25
+ li $26,0xdead0000+26
+ li $27,0xdead0000+27
+ li $28,0xdead0000+28
+ # don't trash the stack otherwise the signal handler won't run
+ #li $29,0xdead0000+29
+ li $30,0xdead0000+30
+ li $31,0xdead0000+31
+
+ lw $zero,($0)
+ b .
+
+
+crashnostack:
+ li $sp, 0
+ lw $zero,($0)
+ b .
diff --git a/debuggerd/crasher/mips64/crashglue.S b/debuggerd/crasher/mips64/crashglue.S
new file mode 100644
index 000000000..70a664127
--- /dev/null
+++ b/debuggerd/crasher/mips64/crashglue.S
@@ -0,0 +1,48 @@
+ .set noat
+
+ .globl crash1
+ .globl crashnostack
+
+crash1:
+ li $0,0xdead0000+0
+ li $1,0xdead0000+1
+ li $2,0xdead0000+2
+ li $3,0xdead0000+3
+ li $4,0xdead0000+4
+ li $5,0xdead0000+5
+ li $6,0xdead0000+6
+ li $7,0xdead0000+7
+ li $8,0xdead0000+8
+ li $9,0xdead0000+9
+ li $10,0xdead0000+10
+ li $11,0xdead0000+11
+ li $12,0xdead0000+12
+ li $13,0xdead0000+13
+ li $14,0xdead0000+14
+ li $15,0xdead0000+15
+ li $16,0xdead0000+16
+ li $17,0xdead0000+17
+ li $18,0xdead0000+18
+ li $19,0xdead0000+19
+ li $20,0xdead0000+20
+ li $21,0xdead0000+21
+ li $22,0xdead0000+22
+ li $23,0xdead0000+23
+ li $24,0xdead0000+24
+ li $25,0xdead0000+25
+ li $26,0xdead0000+26
+ li $27,0xdead0000+27
+ li $28,0xdead0000+28
+ # don't trash the stack otherwise the signal handler won't run
+ #li $29,0xdead0000+29
+ li $30,0xdead0000+30
+ li $31,0xdead0000+31
+
+ lw $zero,($0)
+ b .
+
+
+crashnostack:
+ li $sp, 0
+ lw $zero,($0)
+ b .
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index abda0713a..6a8cc563d 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-#include <dirent.h>
-#include <dlfcn.h>
#include <err.h>
#include <fcntl.h>
-#include <malloc.h>
#include <stdlib.h>
#include <sys/capability.h>
-#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
@@ -31,13 +27,10 @@
#include <chrono>
#include <regex>
-#include <string>
#include <thread>
#include <android/fdsan.h>
#include <android/set_abort_message.h>
-#include <bionic/malloc.h>
-#include <bionic/mte.h>
#include <bionic/reserved_signals.h>
#include <android-base/cmsg.h>
@@ -51,14 +44,12 @@
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
-#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libminijail.h>
#include <scoped_minijail.h>
#include "debuggerd/handler.h"
-#include "libdebuggerd/utility.h"
#include "protocol.h"
#include "tombstoned/tombstoned.h"
#include "util.h"
@@ -67,7 +58,6 @@ using namespace std::chrono_literals;
using android::base::SendFileDescriptors;
using android::base::unique_fd;
-using ::testing::HasSubstr;
#if defined(__LP64__)
#define ARCH_SUFFIX "64"
@@ -75,7 +65,7 @@ using ::testing::HasSubstr;
#define ARCH_SUFFIX ""
#endif
-constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger";
+constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
#define TIMEOUT(seconds, expr) \
[&]() { \
@@ -104,13 +94,6 @@ constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger";
ASSERT_MATCH(result, \
R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
-// Enable GWP-ASan at the start of this process. GWP-ASan is enabled using
-// process sampling, so we need to ensure we force GWP-ASan on.
-__attribute__((constructor)) static void enable_gwp_asan() {
- bool force = true;
- android_mallopt(M_INITIALIZE_GWP_ASAN, &force, sizeof(force));
-}
-
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
InterceptStatus* status, DebuggerdDumpType intercept_type) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
@@ -170,7 +153,7 @@ static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, uniq
class CrasherTest : public ::testing::Test {
public:
pid_t crasher_pid = -1;
- bool previous_wait_for_debugger;
+ bool previous_wait_for_gdb;
unique_fd crasher_pipe;
unique_fd intercept_fd;
@@ -186,18 +169,11 @@ class CrasherTest : public ::testing::Test {
void StartCrasher(const std::string& crash_type);
void FinishCrasher();
void AssertDeath(int signo);
-
- static void Trap(void* ptr);
};
CrasherTest::CrasherTest() {
- previous_wait_for_debugger = android::base::GetBoolProperty(kWaitForDebuggerKey, false);
- android::base::SetProperty(kWaitForDebuggerKey, "0");
-
- // Clear the old property too, just in case someone's been using it
- // on this device. (We only document the new name, but we still support
- // the old name so we don't break anyone's existing setups.)
- android::base::SetProperty("debug.debuggerd.wait_for_gdb", "0");
+ previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
+ android::base::SetProperty(kWaitForGdbKey, "0");
}
CrasherTest::~CrasherTest() {
@@ -207,7 +183,7 @@ CrasherTest::~CrasherTest() {
TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED));
}
- android::base::SetProperty(kWaitForDebuggerKey, previous_wait_for_debugger ? "1" : "0");
+ android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
}
void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {
@@ -279,7 +255,7 @@ void CrasherTest::AssertDeath(int signo) {
}
if (signo == 0) {
- ASSERT_TRUE(WIFEXITED(status)) << "Terminated due to unexpected signal " << WTERMSIG(status);
+ ASSERT_TRUE(WIFEXITED(status));
ASSERT_EQ(0, WEXITSTATUS(signo));
} else {
ASSERT_FALSE(WIFEXITED(status));
@@ -310,19 +286,6 @@ static void ConsumeFd(unique_fd fd, std::string* output) {
*output = std::move(result);
}
-class LogcatCollector {
- public:
- LogcatCollector() { system("logcat -c"); }
-
- void Collect(std::string* output) {
- FILE* cmd_stdout = popen("logcat -d '*:S DEBUG'", "r");
- ASSERT_NE(cmd_stdout, nullptr);
- unique_fd tmp_fd(TEMP_FAILURE_RETRY(dup(fileno(cmd_stdout))));
- ConsumeFd(std::move(tmp_fd), output);
- pclose(cmd_stdout);
- }
-};
-
TEST_F(CrasherTest, smoke) {
int intercept_result;
unique_fd output_fd;
@@ -340,536 +303,6 @@ TEST_F(CrasherTest, smoke) {
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
-
- if (mte_supported()) {
- // Test that the default TAGGED_ADDR_CTRL value is set.
- ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)");
- }
-}
-
-TEST_F(CrasherTest, tagged_fault_addr) {
-#if !defined(__aarch64__)
- GTEST_SKIP() << "Requires aarch64";
-#endif
- int intercept_result;
- unique_fd output_fd;
- StartProcess([]() {
- *reinterpret_cast<volatile char*>(0x100000000000dead) = '1';
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- // The address can either be tagged (new kernels) or untagged (old kernels).
- ASSERT_MATCH(
- result,
- R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
-}
-
-// Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the
-// compiler could still clobber the argument register before trapping, but that's unlikely.
-__attribute__((weak)) void CrasherTest::Trap(void* ptr ATTRIBUTE_UNUSED) {
- __builtin_trap();
-}
-
-TEST_F(CrasherTest, heap_addr_in_register) {
-#if defined(__i386__)
- GTEST_SKIP() << "architecture does not pass arguments in registers";
-#endif
- int intercept_result;
- unique_fd output_fd;
- StartProcess([]() {
- // Crash with a heap pointer in the first argument register.
- Trap(malloc(1));
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- int status;
- ASSERT_EQ(crasher_pid, TIMEOUT(30, waitpid(crasher_pid, &status, 0)));
- ASSERT_TRUE(WIFSIGNALED(status)) << "crasher didn't terminate via a signal";
- // Don't test the signal number because different architectures use different signals for
- // __builtin_trap().
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
-#if defined(__aarch64__)
- ASSERT_MATCH(result, "memory near x0 \\(\\[anon:");
-#elif defined(__arm__)
- ASSERT_MATCH(result, "memory near r0 \\(\\[anon:");
-#elif defined(__x86_64__)
- ASSERT_MATCH(result, "memory near rdi \\(\\[anon:");
-#else
- ASSERT_TRUE(false) << "unsupported architecture";
-#endif
-}
-
-#if defined(__aarch64__)
-static void SetTagCheckingLevelSync() {
- if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_SYNC) == 0) {
- abort();
- }
-}
-#endif
-
-// Number of iterations required to reliably guarantee a GWP-ASan crash.
-// GWP-ASan's sample rate is not truly nondeterministic, it initialises a
-// thread-local counter at 2*SampleRate, and decrements on each malloc(). Once
-// the counter reaches zero, we provide a sampled allocation. Then, double that
-// figure to allow for left/right allocation alignment, as this is done randomly
-// without bias.
-#define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x20000)
-
-struct GwpAsanTestParameters {
- size_t alloc_size;
- bool free_before_access;
- int access_offset;
- std::string cause_needle; // Needle to be found in the "Cause: [GWP-ASan]" line.
-};
-
-struct GwpAsanCrasherTest : CrasherTest, testing::WithParamInterface<GwpAsanTestParameters> {};
-
-GwpAsanTestParameters gwp_asan_tests[] = {
- {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0, "Use After Free, 0 bytes into a 7-byte allocation"},
- {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 1, "Use After Free, 1 byte into a 7-byte allocation"},
- {/* alloc_size */ 7, /* free_before_access */ false, /* access_offset */ 16, "Buffer Overflow, 9 bytes right of a 7-byte allocation"},
- {/* alloc_size */ 16, /* free_before_access */ false, /* access_offset */ -1, "Buffer Underflow, 1 byte left of a 16-byte allocation"},
-};
-
-INSTANTIATE_TEST_SUITE_P(GwpAsanTests, GwpAsanCrasherTest, testing::ValuesIn(gwp_asan_tests));
-
-TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) {
- if (mte_supported()) {
- // Skip this test on MTE hardware, as MTE will reliably catch these errors
- // instead of GWP-ASan.
- GTEST_SKIP() << "Skipped on MTE.";
- }
-
- GwpAsanTestParameters params = GetParam();
- LogcatCollector logcat_collector;
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&params]() {
- for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i) {
- volatile char* p = reinterpret_cast<volatile char*>(malloc(params.alloc_size));
- if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
- p[params.access_offset] = 42;
- if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
- }
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::vector<std::string> log_sources(2);
- ConsumeFd(std::move(output_fd), &log_sources[0]);
- logcat_collector.Collect(&log_sources[1]);
-
- for (const auto& result : log_sources) {
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
- ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
- if (params.free_before_access) {
- ASSERT_MATCH(result, R"(deallocated by thread .*\n.*#00 pc)");
- }
- ASSERT_MATCH(result, R"((^|\s)allocated by thread .*\n.*#00 pc)");
- }
-}
-
-struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
-
-INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(0, 16, 131072));
-
-TEST_P(SizeParamCrasherTest, mte_uaf) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- // Any UAF on a zero-sized allocation will be out-of-bounds so it won't be reported.
- if (GetParam() == 0) {
- return;
- }
-
- LogcatCollector logcat_collector;
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- volatile int* p = (volatile int*)malloc(GetParam());
- free((void *)p);
- p[0] = 42;
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::vector<std::string> log_sources(2);
- ConsumeFd(std::move(output_fd), &log_sources[0]);
- logcat_collector.Collect(&log_sources[1]);
- // Tag dump only available in the tombstone, not logcat.
- ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
-
- for (const auto& result : log_sources) {
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
- ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
- std::to_string(GetParam()) + R"(-byte allocation)");
- ASSERT_MATCH(result, R"(deallocated by thread .*?\n.*#00 pc)");
- ASSERT_MATCH(result, R"((^|\s)allocated by thread .*?\n.*#00 pc)");
- }
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_P(SizeParamCrasherTest, mte_oob_uaf) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- volatile int* p = (volatile int*)malloc(GetParam());
- free((void *)p);
- p[-1] = 42;
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
- ASSERT_NOT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 4 bytes left)");
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_P(SizeParamCrasherTest, mte_overflow) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- LogcatCollector logcat_collector;
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- volatile char* p = (volatile char*)malloc(GetParam());
- p[GetParam()] = 42;
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::vector<std::string> log_sources(2);
- ConsumeFd(std::move(output_fd), &log_sources[0]);
- logcat_collector.Collect(&log_sources[1]);
-
- // Tag dump only in tombstone, not logcat, and tagging is not used for
- // overflow protection in the scudo secondary (guard pages are used instead).
- if (GetParam() < 0x10000) {
- ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
- }
-
- for (const auto& result : log_sources) {
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
- ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
- std::to_string(GetParam()) + R"(-byte allocation)");
- ASSERT_MATCH(result, R"((^|\s)allocated by thread .*?\n.*#00 pc)");
- }
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_P(SizeParamCrasherTest, mte_underflow) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- volatile int* p = (volatile int*)malloc(GetParam());
- p[-1] = 42;
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
- ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a )" +
- std::to_string(GetParam()) + R"(-byte allocation)");
- ASSERT_MATCH(result, R"((^|\s)allocated by thread .*
- #00 pc)");
- ASSERT_MATCH(result, "Memory tags around the fault address");
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_F(CrasherTest, mte_multiple_causes) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- LogcatCollector logcat_collector;
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([]() {
- SetTagCheckingLevelSync();
-
- // Make two allocations with the same tag and close to one another. Check for both properties
- // with a bounds check -- this relies on the fact that only if the allocations have the same tag
- // would they be measured as closer than 128 bytes to each other. Otherwise they would be about
- // (some non-zero value << 56) apart.
- //
- // The out-of-bounds access will be considered either an overflow of one or an underflow of the
- // other.
- std::set<uintptr_t> allocs;
- for (int i = 0; i != 4096; ++i) {
- uintptr_t alloc = reinterpret_cast<uintptr_t>(malloc(16));
- auto it = allocs.insert(alloc).first;
- if (it != allocs.begin() && *std::prev(it) + 128 > alloc) {
- *reinterpret_cast<int*>(*std::prev(it) + 16) = 42;
- }
- if (std::next(it) != allocs.end() && alloc + 128 > *std::next(it)) {
- *reinterpret_cast<int*>(alloc + 16) = 42;
- }
- }
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::vector<std::string> log_sources(2);
- ConsumeFd(std::move(output_fd), &log_sources[0]);
- logcat_collector.Collect(&log_sources[1]);
-
- // Tag dump only in the tombstone, not logcat.
- ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
-
- for (const auto& result : log_sources) {
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
- ASSERT_THAT(result, HasSubstr("Note: multiple potential causes for this crash were detected, "
- "listing them in decreasing order of probability."));
- // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
- // overflows), so we can't match explicitly for an underflow message.
- ASSERT_MATCH(result,
- R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
- // Ensure there's at least two allocation traces (one for each cause).
- ASSERT_MATCH(
- result,
- R"((^|\s)allocated by thread .*?\n.*#00 pc(.|\n)*?(^|\s)allocated by thread .*?\n.*#00 pc)");
- }
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-#if defined(__aarch64__)
-static uintptr_t CreateTagMapping() {
- // Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
- // of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
- size_t page_size = getpagesize();
- void* mapping = mmap(nullptr, page_size * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- uintptr_t mapping_uptr = reinterpret_cast<uintptr_t>(mapping);
- if (mapping == MAP_FAILED) {
- return 0;
- }
- mprotect(reinterpret_cast<void*>(mapping_uptr + page_size), page_size,
- PROT_READ | PROT_WRITE | PROT_MTE);
- // Stripe the mapping, where even granules get tag '1', and odd granules get tag '0'.
- for (uintptr_t offset = 0; offset < page_size; offset += 2 * kTagGranuleSize) {
- uintptr_t tagged_addr = mapping_uptr + page_size + offset + (1ULL << 56);
- __asm__ __volatile__(".arch_extension mte; stg %0, [%0]" : : "r"(tagged_addr) : "memory");
- }
- return mapping_uptr + page_size;
-}
-#endif
-
-TEST_F(CrasherTest, mte_register_tag_dump) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- Trap(reinterpret_cast<void *>(CreateTagMapping()));
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGTRAP);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- ASSERT_MATCH(result, R"(memory near x0:
-.*
-.*
- 01.............0 0000000000000000 0000000000000000 ................
- 00.............0)");
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
- p[0] = 0; // Untagged pointer, tagged memory.
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- ASSERT_MATCH(result, R"(Memory tags around the fault address.*
-\s*=>0x[0-9a-f]+000:\[1\] 0 1 0)");
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_F(CrasherTest, mte_fault_tag_dump) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
- p[320] = 0; // Untagged pointer, tagged memory.
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- ASSERT_MATCH(result, R"(Memory tags around the fault address.*
-\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
-\s*=>0x[0-9a-f]+: 1 0 1 0 \[1\] 0 1 0 1 0 1 0 1 0 1 0
-\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
-)");
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
-TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {
-#if defined(__aarch64__)
- if (!mte_supported()) {
- GTEST_SKIP() << "Requires MTE";
- }
-
- int intercept_result;
- unique_fd output_fd;
- StartProcess([&]() {
- SetTagCheckingLevelSync();
- size_t page_size = getpagesize();
- volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
- p[page_size - kTagGranuleSize * 2] = 0; // Untagged pointer, tagged memory.
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
-
- ASSERT_MATCH(result, R"(Memory tags around the fault address)");
- ASSERT_MATCH(result,
- R"(\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
-\s*=>0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 \[1\] 0
-
-)"); // Ensure truncation happened and there's a newline after the tag fault.
-#else
- GTEST_SKIP() << "Requires aarch64";
-#endif
}
TEST_F(CrasherTest, LD_PRELOAD) {
@@ -999,9 +432,9 @@ TEST_F(CrasherTest, intercept_timeout) {
AssertDeath(SIGABRT);
}
-TEST_F(CrasherTest, wait_for_debugger) {
- if (!android::base::SetProperty(kWaitForDebuggerKey, "1")) {
- FAIL() << "failed to enable wait_for_debugger";
+TEST_F(CrasherTest, wait_for_gdb) {
+ if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
+ FAIL() << "failed to enable wait_for_gdb";
}
sleep(1);
@@ -1582,11 +1015,11 @@ TEST(tombstoned, java_trace_intercept_smoke) {
tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
ASSERT_EQ(InterceptStatus::kRegistered, status);
- // First connect to tombstoned requesting a native tombstone. This
+ // First connect to tombstoned requesting a native backtrace. This
// should result in a "regular" FD and not the installed intercept.
const char native[] = "native";
unique_fd tombstoned_socket, input_fd;
- ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdTombstone));
+ ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace));
ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));
tombstoned_notify_completion(tombstoned_socket.get());
@@ -1698,130 +1131,3 @@ TEST_F(CrasherTest, stack_overflow) {
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
}
-
-static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
- std::string test_lib(testing::internal::GetArgvs()[0]);
- auto const value = test_lib.find_last_of('/');
- if (value == std::string::npos) {
- test_lib = "./";
- } else {
- test_lib = test_lib.substr(0, value + 1) + "./";
- }
- test_lib += "libcrash_test.so";
-
- *tmp_so_name = std::string(tmp_dir) + "/libcrash_test.so";
- std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
-
- // Copy the shared so to a tempory directory.
- return system(cp_cmd.c_str()) == 0;
-}
-
-TEST_F(CrasherTest, unreadable_elf) {
- int intercept_result;
- unique_fd output_fd;
- StartProcess([]() {
- TemporaryDir td;
- std::string tmp_so_name;
- if (!CopySharedLibrary(td.path, &tmp_so_name)) {
- _exit(1);
- }
- void* handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
- if (handle == nullptr) {
- _exit(1);
- }
- // Delete the original shared library so that we get the warning
- // about unreadable elf files.
- if (unlink(tmp_so_name.c_str()) == -1) {
- _exit(1);
- }
- void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, "crash"));
- if (crash_func == nullptr) {
- _exit(1);
- }
- crash_func();
- });
-
- StartIntercept(&output_fd);
- FinishCrasher();
- AssertDeath(SIGSEGV);
- FinishIntercept(&intercept_result);
-
- ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
- std::string result;
- ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(NOTE: Function names and BuildId information is missing )");
-}
-
-TEST(tombstoned, proto) {
- const pid_t self = getpid();
- unique_fd tombstoned_socket, text_fd, proto_fd;
- ASSERT_TRUE(
- tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto));
-
- tombstoned_notify_completion(tombstoned_socket.get());
-
- ASSERT_NE(-1, text_fd.get());
- ASSERT_NE(-1, proto_fd.get());
-
- struct stat text_st;
- ASSERT_EQ(0, fstat(text_fd.get(), &text_st));
-
- // Give tombstoned some time to link the files into place.
- std::this_thread::sleep_for(100ms);
-
- // Find the tombstone.
- std::optional<std::string> tombstone_file;
- std::unique_ptr<DIR, decltype(&closedir)> dir_h(opendir("/data/tombstones"), closedir);
- ASSERT_TRUE(dir_h != nullptr);
- std::regex tombstone_re("tombstone_\\d+");
- dirent* entry;
- while ((entry = readdir(dir_h.get())) != nullptr) {
- if (!std::regex_match(entry->d_name, tombstone_re)) {
- continue;
- }
- std::string path = android::base::StringPrintf("/data/tombstones/%s", entry->d_name);
-
- struct stat st;
- if (TEMP_FAILURE_RETRY(stat(path.c_str(), &st)) != 0) {
- continue;
- }
-
- if (st.st_dev == text_st.st_dev && st.st_ino == text_st.st_ino) {
- tombstone_file = path;
- break;
- }
- }
-
- ASSERT_TRUE(tombstone_file);
- std::string proto_path = tombstone_file.value() + ".pb";
-
- struct stat proto_fd_st;
- struct stat proto_file_st;
- ASSERT_EQ(0, fstat(proto_fd.get(), &proto_fd_st));
- ASSERT_EQ(0, stat(proto_path.c_str(), &proto_file_st));
-
- ASSERT_EQ(proto_fd_st.st_dev, proto_file_st.st_dev);
- ASSERT_EQ(proto_fd_st.st_ino, proto_file_st.st_ino);
-}
-
-TEST(tombstoned, proto_intercept) {
- const pid_t self = getpid();
- unique_fd intercept_fd, output_fd;
- InterceptStatus status;
-
- tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
-
- unique_fd tombstoned_socket, text_fd, proto_fd;
- ASSERT_TRUE(
- tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto));
- ASSERT_TRUE(android::base::WriteStringToFd("foo", text_fd.get()));
- tombstoned_notify_completion(tombstoned_socket.get());
-
- text_fd.reset();
-
- std::string output;
- ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output));
- ASSERT_EQ("foo", output);
-}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index f61578068..9bcbdb36a 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -82,31 +82,29 @@ static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
- thread.registers.reset(
- unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
+ unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
+ thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
// TODO: Create this once and store it in a global?
unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
- // Do not use the thread cache here because it will call pthread_key_create
- // which doesn't work in linker code. See b/189803009.
- // Use a normal cached object because the process is stopped, and there
- // is no chance of data changing between reads.
- auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
- unwinder.SetProcessMemory(process_memory);
- dump_backtrace_thread(output_fd, &unwinder, thread);
+ if (unwinder.Init(arch)) {
+ dump_backtrace_thread(output_fd, &unwinder, thread);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
+ }
}
__linker_disable_fallback_allocator();
}
-static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t* ucontext,
- siginfo_t* siginfo, void* abort_message) {
+static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo,
+ void* abort_message) {
if (!__linker_enable_fallback_allocator()) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
return;
}
- engrave_tombstone_ucontext(output_fd, proto_fd, reinterpret_cast<uintptr_t>(abort_message),
- siginfo, ucontext);
+ engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo,
+ ucontext);
__linker_disable_fallback_allocator();
}
@@ -238,10 +236,7 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
- if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
- kDebuggerdNativeBacktrace)) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc",
- "missing crash_dump_fallback() in selinux policy?");
+ if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
goto exit;
}
@@ -332,10 +327,10 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes
_exit(1);
}
- unique_fd tombstone_socket, output_fd, proto_fd;
- bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd,
- kDebuggerdTombstoneProto);
- debuggerd_fallback_tombstone(output_fd.get(), proto_fd.get(), ucontext, info, abort_message);
+ unique_fd tombstone_socket, output_fd;
+ bool tombstoned_connected =
+ tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
+ debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
if (tombstoned_connected) {
tombstoned_notify_completion(tombstone_socket.get());
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index b60739794..8b4b6304f 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -53,6 +53,7 @@
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
+#include <cutils/properties.h>
#include <libdebuggerd/utility.h>
@@ -82,7 +83,7 @@ using unique_fd = android::base::unique_fd_impl<FdsanBypassCloser>;
#define CRASH_DUMP_NAME "crash_dump32"
#endif
-#define CRASH_DUMP_PATH "/apex/com.android.runtime/bin/" CRASH_DUMP_NAME
+#define CRASH_DUMP_PATH "/system/bin/" CRASH_DUMP_NAME
// Wrappers that directly invoke the respective syscalls, in case the cached values are invalid.
#pragma GCC poison getpid gettid
@@ -274,7 +275,7 @@ static void create_vm_process() {
// There appears to be a bug in the kernel where our death causes SIGHUP to
// be sent to our process group if we exit while it has stopped jobs (e.g.
- // because of wait_for_debugger). Use setsid to create a new process group to
+ // because of wait_for_gdb). Use setsid to create a new process group to
// avoid hitting this.
setsid();
@@ -296,7 +297,10 @@ struct debugger_thread_info {
pid_t pseudothread_tid;
siginfo_t* siginfo;
void* ucontext;
- debugger_process_info process_info;
+ uintptr_t abort_msg;
+ uintptr_t fdsan_table;
+ uintptr_t gwp_asan_state;
+ uintptr_t gwp_asan_metadata;
};
// Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -312,7 +316,7 @@ static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info)
return kDebuggerdNativeBacktrace;
}
- return kDebuggerdTombstoneProto;
+ return kDebuggerdTombstone;
}
static int debuggerd_dispatch_pseudothread(void* arg) {
@@ -340,36 +344,25 @@ static int debuggerd_dispatch_pseudothread(void* arg) {
fatal_errno("failed to create pipe");
}
- uint32_t version;
- ssize_t expected;
-
// ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
- struct iovec iovs[4] = {
- {.iov_base = &version, .iov_len = sizeof(version)},
- {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
- {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
- };
+ uint32_t version = 3;
+ constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
- if (thread_info->process_info.fdsan_table) {
- // Dynamic executables always use version 4. There is no need to increment the version number if
- // the format changes, because the sender (linker) and receiver (crash_dump) are version locked.
- version = 4;
- expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
-
- iovs[3] = {.iov_base = &thread_info->process_info,
- .iov_len = sizeof(thread_info->process_info)};
- } else {
- // Static executables always use version 1.
- version = 1;
- expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
-
- iovs[3] = {.iov_base = &thread_info->process_info.abort_msg, .iov_len = sizeof(uintptr_t)};
- }
errno = 0;
if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
fatal_errno("failed to set pipe buffer size");
}
+ struct iovec iovs[] = {
+ {.iov_base = &version, .iov_len = sizeof(version)},
+ {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
+ {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
+ {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
+ };
+
ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
if (rc == -1) {
fatal_errno("failed to write crash info");
@@ -415,29 +408,24 @@ static int debuggerd_dispatch_pseudothread(void* arg) {
// us to fork off a process to read memory from.
char buf[4];
rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));
-
- bool success = false;
- if (rc == 1 && buf[0] == '\1') {
- // crash_dump successfully started, and is ptracing us.
- // Fork off a copy of our address space for it to use.
- create_vm_process();
- success = true;
- } else {
- // Something went wrong, log it.
- if (rc == -1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
- strerror(errno));
- } else if (rc == 0) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc",
- "crash_dump helper failed to exec, or was killed");
- } else if (rc != 1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc",
- "read of IPC pipe returned unexpected value: %zd", rc);
- } else if (buf[0] != '\1') {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
- }
+ if (rc == -1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+ return 1;
+ } else if (rc == 0) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+ return 1;
+ } else if (rc != 1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
+ return 1;
+ } else if (buf[0] != '\1') {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ return 1;
}
+ // crash_dump is ptracing us, fork off a copy of our address space for it to use.
+ create_vm_process();
+
// Don't leave a zombie child.
int status;
if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {
@@ -447,16 +435,14 @@ static int debuggerd_dispatch_pseudothread(void* arg) {
async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
}
- if (success) {
- if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {
- // For crashes, we don't need to minimize pause latency.
- // Wait for the dump to complete before having the process exit, to avoid being murdered by
- // ActivityManager or init.
- TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf)));
- }
+ if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {
+ // For crashes, we don't need to minimize pause latency.
+ // Wait for the dump to complete before having the process exit, to avoid being murdered by
+ // ActivityManager or init.
+ TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf)));
}
- return success ? 0 : 1;
+ return 0;
}
static void resend_signal(siginfo_t* info) {
@@ -482,8 +468,6 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
// making a syscall and checking errno.
ErrnoRestorer restorer;
- auto *ucontext = static_cast<ucontext_t*>(context);
-
// It's possible somebody cleared the SA_SIGINFO flag, which would mean
// our "info" arg holds an undefined value.
if (!have_siginfo(signal_number)) {
@@ -505,19 +489,29 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
// check to allow all si_code values in calls coming from inside the house.
}
- debugger_process_info process_info = {};
+ void* abort_message = nullptr;
+ const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
+ const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
// Allow for the abort message to be explicitly specified via the sigqueue value.
// Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
- process_info.abort_msg = reinterpret_cast<void*>(si_val & ~1);
+ abort_message = reinterpret_cast<void*>(si_val & ~1);
info->si_ptr = reinterpret_cast<void*>(si_val & 1);
}
}
- } else if (g_callbacks.get_process_info) {
- process_info = g_callbacks.get_process_info();
+ } else {
+ if (g_callbacks.get_abort_message) {
+ abort_message = g_callbacks.get_abort_message();
+ }
+ if (g_callbacks.get_gwp_asan_state) {
+ gwp_asan_state = g_callbacks.get_gwp_asan_state();
+ }
+ if (g_callbacks.get_gwp_asan_metadata) {
+ gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
+ }
}
// If sival_int is ~0, it means that the fallback handler has been called
@@ -530,7 +524,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
// This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
// ANR trace.
- debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
+ debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message);
resend_signal(info);
return;
}
@@ -549,7 +543,10 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
.pseudothread_tid = -1,
.siginfo = info,
.ucontext = context,
- .process_info = process_info,
+ .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
+ .fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
+ .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
+ .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
};
// Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
@@ -600,7 +597,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
// starting to dump right before our death.
pthread_mutex_unlock(&crash_mutex);
} else {
- // Resend the signal, so that either the debugger or the parent's waitpid sees it.
+ // Resend the signal, so that either gdb or the parent's waitpid sees it.
resend_signal(info);
}
}
@@ -636,11 +633,5 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) {
// Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK;
-
-#define SA_EXPOSE_TAGBITS 0x00000800
- // Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE
- // faults.
- action.sa_flags |= SA_EXPOSE_TAGBITS;
-
debuggerd_register_handlers(&action);
}
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index bc08327a1..665d24a7f 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -33,23 +33,13 @@ struct AllocatorState;
struct AllocationMetadata;
}; // namespace gwp_asan
-// When updating this data structure, CrashInfoDataDynamic and the code in
-// ReadCrashInfo() must also be updated.
-struct debugger_process_info {
- void* abort_msg;
- void* fdsan_table;
- const gwp_asan::AllocatorState* gwp_asan_state;
- const gwp_asan::AllocationMetadata* gwp_asan_metadata;
- const char* scudo_stack_depot;
- const char* scudo_region_info;
- const char* scudo_ring_buffer;
-};
-
// These callbacks are called in a signal handler, and thus must be async signal safe.
// If null, the callbacks will not be called.
typedef struct {
- debugger_process_info (*get_process_info)();
+ struct abort_msg_t* (*get_abort_message)();
void (*post_dump)();
+ const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)();
+ const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)();
} debuggerd_callbacks_t;
void debuggerd_init(debuggerd_callbacks_t* callbacks);
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index fd9103824..c60697099 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -18,9 +18,8 @@
#include "libdebuggerd/backtrace.h"
-#include <dirent.h>
#include <errno.h>
-#include <inttypes.h>
+#include <dirent.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
@@ -28,27 +27,30 @@
#include <string.h>
#include <sys/ptrace.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#include <map>
#include <memory>
#include <string>
-#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <log/log.h>
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/types.h"
#include "libdebuggerd/utility.h"
-#include "util.h"
-static void dump_process_header(log_t* log, pid_t pid,
- const std::vector<std::string>& command_line) {
- _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, get_timestamp().c_str());
+static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
+ time_t t = time(NULL);
+ struct tm tm;
+ localtime_r(&t, &tm);
+ char timestr[64];
+ strftime(timestr, sizeof(timestr), "%F %T", &tm);
+ _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
- if (!command_line.empty()) {
- _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", android::base::Join(command_line, " ").c_str());
+ if (process_name) {
+ _LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", process_name);
}
_LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
}
@@ -68,11 +70,7 @@ void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
unwinder->SetRegs(thread.registers.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
- _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- _LOG(&log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
- _LOG(&log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
- }
+ _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
return;
}
@@ -91,7 +89,7 @@ void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* u
return;
}
- dump_process_header(&log, target->second.pid, target->second.command_line);
+ dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
dump_backtrace_thread(output_fd.get(), unwinder, target->second);
for (const auto& [tid, info] : thread_info) {
@@ -108,8 +106,9 @@ void dump_backtrace_header(int output_fd) {
log.tfd = output_fd;
log.amfd_data = nullptr;
- pid_t pid = getpid();
- dump_process_header(&log, pid, get_command_line(pid));
+ char process_name[128];
+ read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
+ dump_process_header(&log, getpid(), process_name);
}
void dump_backtrace_footer(int output_fd) {
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 3ee309fc5..53df783dc 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -15,7 +15,6 @@
*/
#include "libdebuggerd/gwp_asan.h"
-#include "libdebuggerd/tombstone.h"
#include "libdebuggerd/utility.h"
#include "gwp_asan/common.h"
@@ -26,8 +25,6 @@
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
-#include "tombstone.pb.h"
-
// Retrieve GWP-ASan state from `state_addr` inside the process at
// `process_memory`. Place the state into `*state`.
static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,
@@ -66,17 +63,18 @@ static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
}
GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
- const ProcessInfo& process_info, const ThreadInfo& thread_info) {
- if (!process_memory || !process_info.gwp_asan_metadata || !process_info.gwp_asan_state) return;
+ uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
+ const ThreadInfo& thread_info) {
+ if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
// Extract the GWP-ASan regions from the dead process.
- if (!retrieve_gwp_asan_state(process_memory, process_info.gwp_asan_state, &state_)) return;
- metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, process_info.gwp_asan_metadata));
+ if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
+ metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
if (!metadata_.get()) return;
// Get the external crash address from the thread info.
crash_address_ = 0u;
- if (process_info.has_fault_address) {
- crash_address_ = process_info.untagged_fault_address;
+ if (signal_has_si_addr(thread_info.siginfo)) {
+ crash_address_ = reinterpret_cast<uintptr_t>(thread_info.siginfo->si_addr);
}
// Ensure the error belongs to GWP-ASan.
@@ -101,67 +99,6 @@ bool GwpAsanCrashData::CrashIsMine() const {
return is_gwp_asan_responsible_;
}
-constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
-
-void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
- if (!CrashIsMine()) {
- ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash.");
- return;
- }
-
- Cause* cause = tombstone->add_causes();
- MemoryError* memory_error = cause->mutable_memory_error();
- HeapObject* heap_object = memory_error->mutable_heap();
-
- memory_error->set_tool(MemoryError_Tool_GWP_ASAN);
- switch (error_) {
- case gwp_asan::Error::USE_AFTER_FREE:
- memory_error->set_type(MemoryError_Type_USE_AFTER_FREE);
- break;
- case gwp_asan::Error::DOUBLE_FREE:
- memory_error->set_type(MemoryError_Type_DOUBLE_FREE);
- break;
- case gwp_asan::Error::INVALID_FREE:
- memory_error->set_type(MemoryError_Type_INVALID_FREE);
- break;
- case gwp_asan::Error::BUFFER_OVERFLOW:
- memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW);
- break;
- case gwp_asan::Error::BUFFER_UNDERFLOW:
- memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW);
- break;
- default:
- memory_error->set_type(MemoryError_Type_UNKNOWN);
- break;
- }
-
- heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));
- heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));
- unwinder->SetDisplayBuildID(true);
-
- std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
-
- heap_object->set_allocation_tid(__gwp_asan_get_allocation_thread_id(responsible_allocation_));
- size_t num_frames =
- __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
- for (size_t i = 0; i != num_frames; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
- BacktraceFrame* f = heap_object->add_allocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
- }
-
- heap_object->set_deallocation_tid(__gwp_asan_get_deallocation_thread_id(responsible_allocation_));
- num_frames =
- __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
- for (size_t i = 0; i != num_frames; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
- BacktraceFrame* f = heap_object->add_deallocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
- }
-
- set_human_readable_cause(cause, crash_address_);
-}
-
void GwpAsanCrashData::DumpCause(log_t* log) const {
if (!CrashIsMine()) {
ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
@@ -183,6 +120,13 @@ void GwpAsanCrashData::DumpCause(log_t* log) const {
uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
+ if (crash_address_ == alloc_address) {
+ // Use After Free on a 41-byte allocation at 0xdeadbeef.
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_string_, alloc_size, alloc_address);
+ return;
+ }
+
uintptr_t diff;
const char* location_str;
@@ -214,6 +158,65 @@ void GwpAsanCrashData::DumpCause(log_t* log) const {
error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
}
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
+ size_t frame_num) {
+ unwindstack::FrameData frame;
+ frame.num = frame_num;
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (!map_info) {
+ frame.rel_pc = pc;
+ return frame;
+ }
+
+ unwindstack::Elf* elf =
+ map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+ // Create registers just to get PC adjustment. Doesn't matter what they point
+ // to.
+ unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+ uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+ relative_pc -= pc_adjustment;
+ // The debug PC may be different if the PC comes from the JIT.
+ uint64_t debug_pc = relative_pc;
+
+ // If we don't have a valid ELF file, check the JIT.
+ if (!elf->valid()) {
+ unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+ uint64_t jit_pc = pc - pc_adjustment;
+ unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ if (jit_elf != nullptr) {
+ debug_pc = jit_pc;
+ elf = jit_elf;
+ }
+ }
+
+ // Copy all the things we need into the frame for symbolization.
+ frame.rel_pc = relative_pc;
+ frame.pc = pc - pc_adjustment;
+ frame.map_name = map_info->name;
+ frame.map_elf_start_offset = map_info->elf_start_offset;
+ frame.map_exact_offset = map_info->offset;
+ frame.map_start = map_info->start;
+ frame.map_end = map_info->end;
+ frame.map_flags = map_info->flags;
+ frame.map_load_bias = elf->GetLoadBias();
+
+ if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ frame.function_name = "";
+ frame.function_offset = 0;
+ }
+ return frame;
+}
+
+constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
+
bool GwpAsanCrashData::HasDeallocationTrace() const {
assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
@@ -226,7 +229,7 @@ void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder*
assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
- std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
+ std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
size_t num_frames =
__gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
@@ -238,8 +241,7 @@ void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder*
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < num_frames; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
- frame_data.num = i;
+ unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
_LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
}
}
@@ -253,7 +255,7 @@ void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* un
assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
- std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
+ std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
size_t num_frames =
__gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
@@ -265,8 +267,7 @@ void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* un
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < num_frames; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
- frame_data.num = i;
+ unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
_LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
}
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index f9c2481a9..aef4c62aa 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -26,9 +26,6 @@
#include "types.h"
#include "utility.h"
-class Cause;
-class Tombstone;
-
class GwpAsanCrashData {
public:
GwpAsanCrashData() = delete;
@@ -41,8 +38,8 @@ class GwpAsanCrashData {
// still be responsible, as it terminates when it detects an internal error
// (double free, invalid free). In these cases, we will retrieve the fault
// address from the GWP-ASan allocator's state.
- GwpAsanCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info,
- const ThreadInfo& thread_info);
+ GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
+ uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
// Is GWP-ASan responsible for this crash.
bool CrashIsMine() const;
@@ -72,8 +69,6 @@ class GwpAsanCrashData {
// HasAllocationTrace() returns true.
void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
- void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
-
protected:
// Is GWP-ASan responsible for this crash.
bool is_gwp_asan_responsible_ = false;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
deleted file mode 100644
index c3b95d608..000000000
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "types.h"
-#include "utility.h"
-
-#include <memory.h>
-
-#include "scudo/interface.h"
-
-class Cause;
-class Tombstone;
-
-class ScudoCrashData {
- public:
- ScudoCrashData() = delete;
- ~ScudoCrashData() = default;
- ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
-
- bool CrashIsMine() const;
-
- void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
- void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
-
- private:
- scudo_error_info error_info_ = {};
- uintptr_t untagged_fault_addr_;
-
- void DumpReport(const scudo_error_report* report, log_t* log,
- unwindstack::Unwinder* unwinder) const;
-
- void FillInCause(Cause* cause, const scudo_error_report* report,
- unwindstack::Unwinder* unwinder) const;
-};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 2331f1e6e..291d994b3 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -21,7 +21,6 @@
#include <stddef.h>
#include <sys/types.h>
-#include <functional>
#include <map>
#include <string>
@@ -31,13 +30,7 @@
#include "types.h"
// Forward declarations
-class BacktraceFrame;
-class Cause;
-class Tombstone;
-
namespace unwindstack {
-struct FrameData;
-class Maps;
class Unwinder;
}
@@ -51,25 +44,18 @@ constexpr size_t kMaxFrames = 256;
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(android::base::unique_fd output_fd, android::base::unique_fd proto_fd,
- unwindstack::Unwinder* unwinder,
- const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
- const ProcessInfo& process_info, OpenFilesList* open_files,
- std::string* amfd_data);
-
-void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
- siginfo_t* siginfo, ucontext_t* ucontext);
+void engrave_tombstone(int tombstone_fd, unwindstack::Unwinder* unwinder,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uint64_t abort_msg_address, std::string* amfd_data);
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
- const ProcessInfo& process_info, const OpenFilesList* open_files);
+void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
+ ucontext_t* ucontext);
-bool tombstone_proto_to_text(
- const Tombstone& tombstone,
- std::function<void(const std::string& line, bool should_log)> callback);
-
-void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
- unwindstack::Maps* maps);
-void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
+void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+ const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
+ uint64_t abort_msg_address, OpenFilesList* open_files,
+ std::string* amfd_data, uintptr_t gwp_asan_state,
+ uintptr_t gwp_asan_metadata);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 086dc97da..eb4b1b844 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -18,13 +18,11 @@
#include <memory>
#include <string>
-#include <vector>
#include <unwindstack/Regs.h>
struct ThreadInfo {
std::unique_ptr<unwindstack::Regs> registers;
- long tagged_addr_ctrl = -1;
pid_t uid;
@@ -32,25 +30,8 @@ struct ThreadInfo {
std::string thread_name;
pid_t pid;
-
- std::vector<std::string> command_line;
- std::string selinux_label;
+ std::string process_name;
int signo = 0;
siginfo_t* siginfo = nullptr;
};
-
-// This struct is written into a pipe from inside the crashing process.
-struct ProcessInfo {
- uintptr_t abort_msg_address = 0;
- uintptr_t fdsan_table_address = 0;
- uintptr_t gwp_asan_state = 0;
- uintptr_t gwp_asan_metadata = 0;
- uintptr_t scudo_stack_depot = 0;
- uintptr_t scudo_region_info = 0;
- uintptr_t scudo_ring_buffer = 0;
-
- bool has_fault_address = false;
- uintptr_t untagged_fault_address = 0;
- uintptr_t maybe_tagged_fault_address = 0;
-};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 24ae16949..75bac87d3 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -81,10 +81,10 @@ class Memory;
void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
-ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,
- unwindstack::Memory* memory);
void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
+void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
+
void drop_capabilities();
bool signal_has_sender(const siginfo_t*, pid_t caller_pid);
@@ -93,11 +93,4 @@ void get_signal_sender(char* buf, size_t n, const siginfo_t*);
const char* get_signame(const siginfo_t*);
const char* get_sigcode(const siginfo_t*);
-// Number of bytes per MTE granule.
-constexpr size_t kTagGranuleSize = 16;
-
-// Number of rows and columns to display in an MTE tag dump.
-constexpr size_t kNumTagColumns = 16;
-constexpr size_t kNumTagRows = 16;
-
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
deleted file mode 100644
index f4690bac3..000000000
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "libdebuggerd/scudo.h"
-#include "libdebuggerd/tombstone.h"
-
-#include "unwindstack/Memory.h"
-#include "unwindstack/Unwinder.h"
-
-#include <android-base/macros.h>
-#include <bionic/macros.h>
-
-#include "tombstone.pb.h"
-
-std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
- size_t size) {
- auto buf = std::make_unique<char[]>(size);
- if (!process_memory->ReadFully(addr, buf.get(), size)) {
- return std::unique_ptr<char[]>();
- }
- return buf;
-}
-
-ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
- const ProcessInfo& process_info) {
- if (!process_info.has_fault_address) {
- return;
- }
-
- auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
- __scudo_get_stack_depot_size());
- auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
- __scudo_get_region_info_size());
- auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
- __scudo_get_ring_buffer_size());
-
- untagged_fault_addr_ = process_info.untagged_fault_address;
- uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
-
- uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
- if (memory_begin > fault_page) {
- return;
- }
-
- uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
- if (memory_end < fault_page) {
- return;
- }
-
- auto memory = std::make_unique<char[]>(memory_end - memory_begin);
- for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
- process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
- }
-
- auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
- for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
- memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
- }
-
- __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
- region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
- memory_begin, memory_end - memory_begin);
-}
-
-bool ScudoCrashData::CrashIsMine() const {
- return error_info_.reports[0].error_type != UNKNOWN;
-}
-
-void ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report,
- unwindstack::Unwinder* unwinder) const {
- MemoryError* memory_error = cause->mutable_memory_error();
- HeapObject* heap_object = memory_error->mutable_heap();
-
- memory_error->set_tool(MemoryError_Tool_SCUDO);
- switch (report->error_type) {
- case USE_AFTER_FREE:
- memory_error->set_type(MemoryError_Type_USE_AFTER_FREE);
- break;
- case BUFFER_OVERFLOW:
- memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW);
- break;
- case BUFFER_UNDERFLOW:
- memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW);
- break;
- default:
- memory_error->set_type(MemoryError_Type_UNKNOWN);
- break;
- }
-
- heap_object->set_address(report->allocation_address);
- heap_object->set_size(report->allocation_size);
- unwinder->SetDisplayBuildID(true);
-
- heap_object->set_allocation_tid(report->allocation_tid);
- for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
- BacktraceFrame* f = heap_object->add_allocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
- }
-
- heap_object->set_deallocation_tid(report->deallocation_tid);
- for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
- BacktraceFrame* f = heap_object->add_deallocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
- }
-
- set_human_readable_cause(cause, untagged_fault_addr_);
-}
-
-void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
- size_t report_num = 0;
- while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
- error_info_.reports[report_num].error_type != UNKNOWN) {
- FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder);
- }
-}
-
-void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
- if (error_info_.reports[1].error_type != UNKNOWN) {
- _LOG(log, logtype::HEADER,
- "\nNote: multiple potential causes for this crash were detected, listing them in "
- "decreasing order of probability.\n");
- }
-
- size_t report_num = 0;
- while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
- error_info_.reports[report_num].error_type != UNKNOWN) {
- DumpReport(&error_info_.reports[report_num++], log, unwinder);
- }
-}
-
-void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
- unwindstack::Unwinder* unwinder) const {
- const char *error_type_str;
- switch (report->error_type) {
- case USE_AFTER_FREE:
- error_type_str = "Use After Free";
- break;
- case BUFFER_OVERFLOW:
- error_type_str = "Buffer Overflow";
- break;
- case BUFFER_UNDERFLOW:
- error_type_str = "Buffer Underflow";
- break;
- default:
- error_type_str = "Unknown";
- break;
- }
-
- uintptr_t diff;
- const char* location_str;
-
- if (untagged_fault_addr_ < report->allocation_address) {
- // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
- location_str = "left of";
- diff = report->allocation_address - untagged_fault_addr_;
- } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
- // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
- location_str = "into";
- diff = untagged_fault_addr_ - report->allocation_address;
- } else {
- // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
- location_str = "right of";
- diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
- }
-
- // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
- const char* byte_suffix = "s";
- if (diff == 1) {
- byte_suffix = "";
- }
- _LOG(log, logtype::HEADER,
- "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
- error_type_str, diff, byte_suffix, location_str, report->allocation_size,
- report->allocation_address);
-
- if (report->allocation_trace[0]) {
- _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
- }
-
- if (report->deallocation_trace[0]) {
- _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
- }
-}
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 8f84346af..023a57807 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -33,7 +33,8 @@ class UnwinderMock : public unwindstack::Unwinder {
void MockSetBuildID(uint64_t offset, const std::string& build_id) {
unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
if (map_info != nullptr) {
- map_info->SetBuildID(std::string(build_id));
+ std::string* new_build_id = new std::string(build_id);
+ map_info->build_id = reinterpret_cast<uintptr_t>(new_build_id);
}
}
};
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 5be145aad..be395824b 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -30,39 +30,39 @@
const char g_expected_full_dump[] =
"\nmemory near r1:\n"
#if defined(__LP64__)
-" 0000000012345650 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 0000000012345660 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 0000000012345670 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 0000000012345680 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 0000000012345690 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 00000000123456a0 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 00000000123456b0 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 00000000123456c0 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 00000000123456d0 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 00000000123456e0 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 00000000123456f0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000012345700 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000012345710 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000012345720 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000012345730 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000012345740 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-" 12345650 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" 12345660 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" 12345670 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" 12345680 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" 12345690 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" 123456a0 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" 123456b0 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" 123456c0 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" 123456d0 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 123456e0 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 123456f0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 12345700 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 12345710 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 12345720 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 12345730 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 12345740 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
const char g_expected_partial_dump[] = \
@@ -73,14 +73,34 @@ const char g_expected_partial_dump[] = \
" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n";
+" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 0000000012345640 6766656463626160 ---------------- `abcdefg........\n"
+" 0000000012345650 ---------------- ---------------- ................\n"
+" 0000000012345660 ---------------- ---------------- ................\n"
+" 0000000012345670 ---------------- ---------------- ................\n"
+" 0000000012345680 ---------------- ---------------- ................\n"
+" 0000000012345690 ---------------- ---------------- ................\n"
+" 00000000123456a0 ---------------- ---------------- ................\n"
+" 00000000123456b0 ---------------- ---------------- ................\n"
+" 00000000123456c0 ---------------- ---------------- ................\n"
+" 00000000123456d0 ---------------- ---------------- ................\n";
#else
" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n";
+" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 12345640 63626160 67666564 -------- -------- `abcdefg........\n"
+" 12345650 -------- -------- -------- -------- ................\n"
+" 12345660 -------- -------- -------- -------- ................\n"
+" 12345670 -------- -------- -------- -------- ................\n"
+" 12345680 -------- -------- -------- -------- ................\n"
+" 12345690 -------- -------- -------- -------- ................\n"
+" 123456a0 -------- -------- -------- -------- ................\n"
+" 123456b0 -------- -------- -------- -------- ................\n"
+" 123456c0 -------- -------- -------- -------- ................\n"
+" 123456d0 -------- -------- -------- -------- ................\n";
#endif
class MemoryMock : public unwindstack::Memory {
@@ -92,10 +112,7 @@ class MemoryMock : public unwindstack::Memory {
if (last_read_addr_ > 0) {
offset = addr - last_read_addr_;
}
- size_t bytes_available = 0;
- if (offset < buffer_.size()) {
- bytes_available = buffer_.size() - offset;
- }
+ size_t bytes_available = buffer_.size() - offset;
if (partial_read_) {
bytes = std::min(bytes, bytes_partial_read_);
@@ -241,7 +258,44 @@ TEST_F(DumpMemoryTest, memory_unreadable) {
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
+ const char* expected_dump = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+" 00000000a2345658 ---------------- ---------------- ................\n"
+" 00000000a2345668 ---------------- ---------------- ................\n"
+" 00000000a2345678 ---------------- ---------------- ................\n"
+" 00000000a2345688 ---------------- ---------------- ................\n"
+" 00000000a2345698 ---------------- ---------------- ................\n"
+" 00000000a23456a8 ---------------- ---------------- ................\n"
+" 00000000a23456b8 ---------------- ---------------- ................\n"
+" 00000000a23456c8 ---------------- ---------------- ................\n"
+" 00000000a23456d8 ---------------- ---------------- ................\n"
+" 00000000a23456e8 ---------------- ---------------- ................\n"
+" 00000000a23456f8 ---------------- ---------------- ................\n"
+" 00000000a2345708 ---------------- ---------------- ................\n"
+" 00000000a2345718 ---------------- ---------------- ................\n"
+" 00000000a2345728 ---------------- ---------------- ................\n"
+" 00000000a2345738 ---------------- ---------------- ................\n"
+" 00000000a2345748 ---------------- ---------------- ................\n";
+#else
+" a2345658 -------- -------- -------- -------- ................\n"
+" a2345668 -------- -------- -------- -------- ................\n"
+" a2345678 -------- -------- -------- -------- ................\n"
+" a2345688 -------- -------- -------- -------- ................\n"
+" a2345698 -------- -------- -------- -------- ................\n"
+" a23456a8 -------- -------- -------- -------- ................\n"
+" a23456b8 -------- -------- -------- -------- ................\n"
+" a23456c8 -------- -------- -------- -------- ................\n"
+" a23456d8 -------- -------- -------- -------- ................\n"
+" a23456e8 -------- -------- -------- -------- ................\n"
+" a23456f8 -------- -------- -------- -------- ................\n"
+" a2345708 -------- -------- -------- -------- ................\n"
+" a2345718 -------- -------- -------- -------- ................\n"
+" a2345728 -------- -------- -------- -------- ................\n"
+" a2345738 -------- -------- -------- -------- ................\n"
+" a2345748 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -375,17 +429,57 @@ TEST_F(DumpMemoryTest, address_low_fence) {
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
+TEST_F(DumpMemoryTest, memory_address_too_low) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+
+ dump_memory(&log_, memory_mock_.get(), 0, "memory near r1");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
TEST_F(DumpMemoryTest, memory_address_too_high) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), -32, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), -208, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near r1");
+#else
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near r1");
+#endif
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_would_overflow) {
+ uint8_t buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+ dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near r1");
#else
- dump_memory(&log_, memory_mock_.get(), 0x100000000 - 32, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0x100000000 - 208, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near r1");
#endif
std::string tombstone_contents;
@@ -406,9 +500,9 @@ TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), -224, "memory near r4");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near r4");
#else
- dump_memory(&log_, memory_mock_.get(), 0x100000000 - 224, "memory near r4");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near r4");
#endif
std::string tombstone_contents;
@@ -416,57 +510,40 @@ TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
"\nmemory near r4:\n"
-#if defined(__aarch64__)
-" 00ffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 00ffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 00ffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 00ffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 00ffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 00ffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 00ffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 00ffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 00ffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 00ffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 00ffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 00ffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 00ffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 00ffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 00ffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 00fffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
-#elif defined(__LP64__)
-" ffffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" ffffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" ffffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" ffffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" ffffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" ffffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" ffffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" ffffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" ffffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" ffffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" ffffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" ffffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" ffffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" ffffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" ffffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" fffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#if defined(__LP64__)
+" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-" ffffff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" ffffff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" ffffff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" ffffff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" ffffff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" ffffff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" ffffff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" ffffff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" ffffff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" ffffff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" ffffffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" ffffffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" ffffffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" ffffffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" ffffffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" fffffff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -493,25 +570,39 @@ TEST_F(DumpMemoryTest, first_read_empty) {
const char* expected_dump = \
"\nmemory near r4:\n"
#if defined(__LP64__)
-R"( 0000000010001000 8786858483828180 8f8e8d8c8b8a8988 ................
- 0000000010001010 9796959493929190 9f9e9d9c9b9a9998 ................
- 0000000010001020 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................
- 0000000010001030 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................
- 0000000010001040 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................
- 0000000010001050 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................
- 0000000010001060 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................
- 0000000010001070 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................
-)";
+" 0000000010000f88 ---------------- ---------------- ................\n"
+" 0000000010000f98 ---------------- ---------------- ................\n"
+" 0000000010000fa8 ---------------- ---------------- ................\n"
+" 0000000010000fb8 ---------------- ---------------- ................\n"
+" 0000000010000fc8 ---------------- ---------------- ................\n"
+" 0000000010000fd8 ---------------- ---------------- ................\n"
+" 0000000010000fe8 ---------------- ---------------- ................\n"
+" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
+" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-R"( 10001000 83828180 87868584 8b8a8988 8f8e8d8c ................
- 10001010 93929190 97969594 9b9a9998 9f9e9d9c ................
- 10001020 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................
- 10001030 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................
- 10001040 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................
- 10001050 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................
- 10001060 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................
- 10001070 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................
-)";
+" 10000f88 -------- -------- -------- -------- ................\n"
+" 10000f98 -------- -------- -------- -------- ................\n"
+" 10000fa8 -------- -------- -------- -------- ................\n"
+" 10000fb8 -------- -------- -------- -------- ................\n"
+" 10000fc8 -------- -------- -------- -------- ................\n"
+" 10000fd8 -------- -------- -------- -------- ................\n"
+" 10000fe8 -------- -------- -------- -------- ................\n"
+" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
+" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -538,11 +629,39 @@ TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
const char* expected_dump = \
"\nmemory near r4:\n"
#if defined(__LP64__)
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n"
" 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n";
+" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001020 ---------------- ---------------- ................\n"
+" 0000000010001030 ---------------- ---------------- ................\n";
#else
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n"
" 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n";
+" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001020 -------- -------- -------- -------- ................\n"
+" 10001030 -------- -------- -------- -------- ................\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -565,7 +684,44 @@ TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000000 ---------------- ---------------- ................\n"
+" 0000000010000010 ---------------- ---------------- ................\n"
+" 0000000010000020 ---------------- ---------------- ................\n"
+" 0000000010000030 ---------------- ---------------- ................\n"
+" 0000000010000040 ---------------- ---------------- ................\n"
+" 0000000010000050 ---------------- ---------------- ................\n"
+" 0000000010000060 ---------------- ---------------- ................\n"
+" 0000000010000070 ---------------- ---------------- ................\n"
+" 0000000010000080 ---------------- ---------------- ................\n"
+" 0000000010000090 ---------------- ---------------- ................\n"
+" 00000000100000a0 ---------------- ---------------- ................\n"
+" 00000000100000b0 ---------------- ---------------- ................\n"
+" 00000000100000c0 ---------------- ---------------- ................\n"
+" 00000000100000d0 ---------------- ---------------- ................\n"
+" 00000000100000e0 ---------------- ---------------- ................\n"
+" 00000000100000f0 ---------------- ---------------- ................\n";
+#else
+" 10000000 -------- -------- -------- -------- ................\n"
+" 10000010 -------- -------- -------- -------- ................\n"
+" 10000020 -------- -------- -------- -------- ................\n"
+" 10000030 -------- -------- -------- -------- ................\n"
+" 10000040 -------- -------- -------- -------- ................\n"
+" 10000050 -------- -------- -------- -------- ................\n"
+" 10000060 -------- -------- -------- -------- ................\n"
+" 10000070 -------- -------- -------- -------- ................\n"
+" 10000080 -------- -------- -------- -------- ................\n"
+" 10000090 -------- -------- -------- -------- ................\n"
+" 100000a0 -------- -------- -------- -------- ................\n"
+" 100000b0 -------- -------- -------- -------- ................\n"
+" 100000c0 -------- -------- -------- -------- ................\n"
+" 100000d0 -------- -------- -------- -------- ................\n"
+" 100000e0 -------- -------- -------- -------- ................\n"
+" 100000f0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -588,7 +744,44 @@ TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f00 ---------------- ---------------- ................\n"
+" 0000000010000f10 ---------------- ---------------- ................\n"
+" 0000000010000f20 ---------------- ---------------- ................\n"
+" 0000000010000f30 ---------------- ---------------- ................\n"
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n";
+#else
+" 10000f00 -------- -------- -------- -------- ................\n"
+" 10000f10 -------- -------- -------- -------- ................\n"
+" 10000f20 -------- -------- -------- -------- ................\n"
+" 10000f30 -------- -------- -------- -------- ................\n"
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
diff --git a/debuggerd/libdebuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
new file mode 100644
index 000000000..1f4f58afc
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/sys/system_properties.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
+
+// This is just enough to get the property code to compile on
+// the host.
+
+#define PROP_VALUE_MAX 92
+
+#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index a14dcb0e7..eed95bc39 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -350,21 +350,28 @@ TEST_F(TombstoneTest, dump_header_info) {
}
TEST_F(TombstoneTest, dump_thread_info_uid) {
- std::vector<std::string> cmdline = {"some_process"};
- dump_thread_info(
- &log_,
- ThreadInfo{
- .uid = 1, .tid = 3, .thread_name = "some_thread", .pid = 2, .command_line = cmdline});
+ dump_thread_info(&log_, ThreadInfo{.uid = 1,
+ .tid = 3,
+ .thread_name = "some_thread",
+ .pid = 2,
+ .process_name = "some_process"});
std::string expected = "pid: 2, tid: 3, name: some_thread >>> some_process <<<\nuid: 1\n";
ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
}
+TEST_F(TombstoneTest, dump_timestamp) {
+ setenv("TZ", "UTC", 1);
+ tzset();
+ dump_timestamp(&log_, 0);
+ ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
+}
+
class GwpAsanCrashDataTest : public GwpAsanCrashData {
public:
GwpAsanCrashDataTest(
gwp_asan::Error error,
const gwp_asan::AllocationMetadata *responsible_allocation) :
- GwpAsanCrashData(nullptr, ProcessInfo{}, ThreadInfo{}) {
+ GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
is_gwp_asan_responsible_ = true;
error_ = error;
responsible_allocation_ = responsible_allocation;
@@ -379,7 +386,7 @@ public:
TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
gwp_asan::AllocationMetadata meta;
meta.Addr = 0x1000;
- meta.RequestedSize = 32;
+ meta.Size = 32;
GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
crash_data.SetCrashAddress(0x1000);
@@ -388,14 +395,15 @@ TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
std::string tombstone_contents;
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free, 0 bytes "
- "into a 32-byte allocation at 0x[a-fA-F0-9]+\n"));
+ ASSERT_THAT(tombstone_contents,
+ MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
}
TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
gwp_asan::AllocationMetadata meta;
meta.Addr = 0x1000;
- meta.RequestedSize = 32;
+ meta.Size = 32;
GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
crash_data.SetCrashAddress(0x1000);
@@ -404,14 +412,15 @@ TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
std::string tombstone_contents;
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free, 0 bytes into a "
- "32-byte allocation at 0x[a-fA-F0-9]+\n"));
+ ASSERT_THAT(tombstone_contents,
+ MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
}
TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
gwp_asan::AllocationMetadata meta;
meta.Addr = 0x1000;
- meta.RequestedSize = 32;
+ meta.Size = 32;
GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
crash_data.SetCrashAddress(0x1025);
@@ -430,7 +439,7 @@ TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
gwp_asan::AllocationMetadata meta;
meta.Addr = 0x1000;
- meta.RequestedSize = 32;
+ meta.Size = 32;
GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
crash_data.SetCrashAddress(0xffe);
@@ -449,7 +458,7 @@ TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
gwp_asan::AllocationMetadata meta;
meta.Addr = 0x1000;
- meta.RequestedSize = 32;
+ meta.Size = 32;
GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
crash_data.SetCrashAddress(0x1001);
@@ -468,7 +477,7 @@ TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
gwp_asan::AllocationMetadata meta;
meta.Addr = 0x1000;
- meta.RequestedSize = 32;
+ meta.Size = 32;
GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
crash_data.SetCrashAddress(0x1021);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 9c01f15eb..fd52e8113 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,15 +36,13 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/log.h>
-#include <async_safe/log.h>
-#include <bionic/macros.h>
#include <log/log.h>
-#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include <unwindstack/DexFiles.h>
@@ -57,15 +55,11 @@
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
-#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
-#include "util.h"
#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
-#include "tombstone.pb.h"
-
using android::base::GetBoolProperty;
using android::base::GetProperty;
using android::base::StringPrintf;
@@ -84,6 +78,15 @@ static void dump_header_info(log_t* log) {
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
+static void dump_timestamp(log_t* log, time_t time) {
+ struct tm tm;
+ localtime_r(&time, &tm);
+
+ char buf[strlen("1970-01-01 00:00:00+0830") + 1];
+ strftime(buf, sizeof(buf), "%F %T%z", &tm);
+ _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
+}
+
static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
unwindstack::Maps* maps) {
static constexpr uint64_t kMaxDifferenceBytes = 256;
@@ -106,9 +109,9 @@ static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
unwindstack::MapInfo* map_info = maps->Find(sp);
if (map_info == nullptr) {
return "stack pointer is in a non-existent map; likely due to stack overflow.";
- } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+ } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
return "stack pointer is not in a rw map; likely due to stack overflow.";
- } else if ((sp - map_info->start()) <= kMaxDifferenceBytes) {
+ } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
return "stack pointer is close to top of stack; likely stack overflow.";
}
}
@@ -137,7 +140,7 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Ma
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
unwindstack::MapInfo* map_info = maps->Find(fault_addr);
- if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
+ if (map_info != nullptr && map_info->flags == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
} else {
cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
@@ -151,18 +154,16 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Ma
}
static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
- const ProcessInfo& process_info, unwindstack::Memory* process_memory) {
+ unwindstack::Memory* process_memory) {
char addr_desc[64]; // ", fault addr 0x1234"
- if (process_info.has_fault_address) {
- // SIGILL faults will never have tagged addresses, so okay to
- // indiscriminately use the tagged address here.
- size_t addr = process_info.maybe_tagged_fault_address;
+ if (signal_has_si_addr(thread_info.siginfo)) {
+ void* addr = thread_info.siginfo->si_addr;
if (thread_info.siginfo->si_signo == SIGILL) {
uint32_t instruction = {};
- process_memory->Read(addr, &instruction, sizeof(instruction));
- snprintf(addr_desc, sizeof(addr_desc), "0x%zx (*pc=%#08x)", addr, instruction);
+ process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
+ snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
} else {
- snprintf(addr_desc, sizeof(addr_desc), "0x%zx", addr);
+ snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
}
} else {
snprintf(addr_desc, sizeof(addr_desc), "--------");
@@ -179,26 +180,23 @@ static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
}
static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
- // Don't try to collect logs from the threads that implement the logging system itself.
- if (thread_info.uid == AID_LOGD) log->should_retrieve_logcat = false;
-
- const char* process_name = "<unknown>";
- if (!thread_info.command_line.empty()) {
- process_name = thread_info.command_line[0].c_str();
+ // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
+ // TODO: Why is this controlled by thread name?
+ if (thread_info.thread_name == "logd" ||
+ android::base::StartsWith(thread_info.thread_name, "logd.")) {
+ log->should_retrieve_logcat = false;
}
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
- thread_info.tid, thread_info.thread_name.c_str(), process_name);
+ thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
_LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
- if (thread_info.tagged_addr_ctrl != -1) {
- _LOG(log, logtype::HEADER, "tagged_addr_ctrl: %016lx\n", thread_info.tagged_addr_ctrl);
- }
}
static std::string get_addr_string(uint64_t addr) {
std::string addr_str;
#if defined(__LP64__)
- addr_str = StringPrintf("%08x'%08x", static_cast<uint32_t>(addr >> 32),
+ addr_str = StringPrintf("%08x'%08x",
+ static_cast<uint32_t>(addr >> 32),
static_cast<uint32_t>(addr & 0xffffffff));
#else
addr_str = StringPrintf("%08x", static_cast<uint32_t>(addr));
@@ -244,7 +242,7 @@ static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t
"memory map (%zu entr%s):",
maps->Total(), maps->Total() == 1 ? "y" : "ies");
if (print_fault_address_marker) {
- if (maps->Total() != 0 && addr < maps->Get(0)->start()) {
+ if (maps->Total() != 0 && addr < maps->Get(0)->start) {
_LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
@@ -261,37 +259,37 @@ static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t
for (auto const& map_info : *maps) {
line = " ";
if (print_fault_address_marker) {
- if (addr < map_info->start()) {
+ if (addr < map_info->start) {
_LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
- } else if (addr >= map_info->start() && addr < map_info->end()) {
+ } else if (addr >= map_info->start && addr < map_info->end) {
line = "--->";
print_fault_address_marker = false;
}
}
- line += get_addr_string(map_info->start()) + '-' + get_addr_string(map_info->end() - 1) + ' ';
- if (map_info->flags() & PROT_READ) {
+ line += get_addr_string(map_info->start) + '-' + get_addr_string(map_info->end - 1) + ' ';
+ if (map_info->flags & PROT_READ) {
line += 'r';
} else {
line += '-';
}
- if (map_info->flags() & PROT_WRITE) {
+ if (map_info->flags & PROT_WRITE) {
line += 'w';
} else {
line += '-';
}
- if (map_info->flags() & PROT_EXEC) {
+ if (map_info->flags & PROT_EXEC) {
line += 'x';
} else {
line += '-';
}
- line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset(),
- map_info->end() - map_info->start());
+ line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset,
+ map_info->end - map_info->start);
bool space_needed = true;
- if (!map_info->name().empty()) {
+ if (!map_info->name.empty()) {
space_needed = false;
- line += " " + map_info->name();
+ line += " " + map_info->name;
std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
line += " (BuildId: " + build_id + ")";
@@ -368,9 +366,9 @@ void dump_memory_and_code(log_t* log, unwindstack::Maps* maps, unwindstack::Memo
regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
std::string label{"memory near "s + reg_name};
if (maps) {
- unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
- if (map_info != nullptr && !map_info->name().empty()) {
- label += " (" + map_info->name() + ")";
+ unwindstack::MapInfo* map_info = maps->Find(reg_value);
+ if (map_info != nullptr && !map_info->name.empty()) {
+ label += " (" + map_info->name + ")";
}
}
dump_memory(log, memory, reg_value, label);
@@ -378,7 +376,8 @@ void dump_memory_and_code(log_t* log, unwindstack::Maps* maps, unwindstack::Memo
}
static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
- const ProcessInfo& process_info, bool primary_thread) {
+ uint64_t abort_msg_address, bool primary_thread,
+ const GwpAsanCrashData& gwp_asan_crash_data) {
log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -386,26 +385,18 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
- dump_signal_info(log, thread_info, process_info, unwinder->GetProcessMemory().get());
- }
-
- std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
- std::unique_ptr<ScudoCrashData> scudo_crash_data;
- if (primary_thread) {
- gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
- process_info, thread_info);
- scudo_crash_data =
- std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
+ dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
}
- if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
- gwp_asan_crash_data->DumpCause(log);
- } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
- dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+ if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
+ gwp_asan_crash_data.DumpCause(log);
+ } else if (thread_info.siginfo) {
+ dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
+ thread_info.registers.get());
}
if (primary_thread) {
- dump_abort_message(log, unwinder->GetProcessMemory().get(), process_info.abort_msg_address);
+ dump_abort_message(log, unwinder->GetProcessMemory().get(), abort_msg_address);
}
dump_registers(log, thread_info.registers.get());
@@ -415,34 +406,29 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa
unwinder->SetRegs(regs_copy.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
- _LOG(log, logtype::THREAD, "Failed to unwind\n");
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- _LOG(log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
- _LOG(log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
- }
+ _LOG(log, logtype::THREAD, "Failed to unwind");
} else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
log_backtrace(log, unwinder, " ");
}
if (primary_thread) {
- if (gwp_asan_crash_data->HasDeallocationTrace()) {
- gwp_asan_crash_data->DumpDeallocationTrace(log, unwinder);
+ if (gwp_asan_crash_data.HasDeallocationTrace()) {
+ gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
}
- if (gwp_asan_crash_data->HasAllocationTrace()) {
- gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
+ if (gwp_asan_crash_data.HasAllocationTrace()) {
+ gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
}
- scudo_crash_data->DumpCause(log, unwinder);
-
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
if (maps != nullptr) {
uint64_t addr = 0;
- if (process_info.has_fault_address) {
- addr = process_info.untagged_fault_address;
+ siginfo_t* si = thread_info.siginfo;
+ if (signal_has_si_addr(si)) {
+ addr = reinterpret_cast<uint64_t>(si->si_addr);
}
dump_all_maps(log, unwinder, addr);
}
@@ -456,6 +442,8 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa
// that don't match the specified pid, and writes them to the tombstone file.
//
// If "tail" is non-zero, log the last "tail" number of lines.
+static EventTagMap* g_eventTagMap = NULL;
+
static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
bool first = true;
logger_list* logger_list;
@@ -464,8 +452,8 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned
return;
}
- logger_list =
- android_logger_list_open(android_name_to_log_id(filename), ANDROID_LOG_NONBLOCK, tail, pid);
+ logger_list = android_logger_list_open(
+ android_name_to_log_id(filename), ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tail, pid);
if (!logger_list) {
ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
@@ -498,7 +486,8 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned
// the tombstone file.
if (first) {
- _LOG(log, logtype::LOGS, "--------- %slog %s\n", tail ? "tail end of " : "", filename);
+ _LOG(log, logtype::LOGS, "--------- %slog %s\n",
+ tail ? "tail end of " : "", filename);
first = false;
}
@@ -508,9 +497,25 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned
// (although in this case the pid is redundant).
char timeBuf[32];
time_t sec = static_cast<time_t>(log_entry.entry.sec);
- tm tm;
- localtime_r(&sec, &tm);
- strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", &tm);
+ struct tm tmBuf;
+ struct tm* ptm;
+ ptm = localtime_r(&sec, &tmBuf);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+ if (log_entry.id() == LOG_ID_EVENTS) {
+ if (!g_eventTagMap) {
+ g_eventTagMap = android_openEventTagMap(nullptr);
+ }
+ AndroidLogEntry e;
+ char buf[512];
+ if (android_log_processBinaryLogBuffer(&log_entry.entry, &e, g_eventTagMap, buf,
+ sizeof(buf)) == 0) {
+ _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
+ log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
+ (int)e.tagLen, e.tag, e.message);
+ }
+ continue;
+ }
char* msg = log_entry.msg();
if (msg == nullptr) {
@@ -559,8 +564,8 @@ static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
dump_log_file(log, pid, "main", tail);
}
-void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
- siginfo_t* siginfo, ucontext_t* ucontext) {
+void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
+ ucontext_t* ucontext) {
pid_t uid = getuid();
pid_t pid = getpid();
pid_t tid = gettid();
@@ -571,55 +576,42 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m
log.tfd = tombstone_fd;
log.amfd_data = nullptr;
- std::string thread_name = get_thread_name(tid);
- std::vector<std::string> command_line = get_command_line(pid);
+ char thread_name[16];
+ char process_name[128];
+
+ read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
+ read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
std::unique_ptr<unwindstack::Regs> regs(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
- std::string selinux_label;
- android::base::ReadFileToString("/proc/self/attr/current", &selinux_label);
-
std::map<pid_t, ThreadInfo> threads;
- threads[tid] = ThreadInfo{
+ threads[gettid()] = ThreadInfo{
.registers = std::move(regs),
.uid = uid,
.tid = tid,
- .thread_name = std::move(thread_name),
+ .thread_name = thread_name,
.pid = pid,
- .command_line = std::move(command_line),
- .selinux_label = std::move(selinux_label),
+ .process_name = process_name,
.siginfo = siginfo,
};
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
- auto process_memory =
- unwindstack::Memory::CreateProcessMemoryCached(getpid());
- unwinder.SetProcessMemory(process_memory);
- if (!unwinder.Init()) {
- async_safe_fatal("failed to init unwinder object");
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
+ if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ LOG(FATAL) << "Failed to init unwinder object.";
}
- ProcessInfo process_info;
- process_info.abort_msg_address = abort_msg_address;
- engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads, tid,
- process_info, nullptr, nullptr);
+ engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
+ nullptr, nullptr, 0u, 0u);
}
-void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::Unwinder* unwinder,
+void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
- const ProcessInfo& process_info, OpenFilesList* open_files,
- std::string* amfd_data) {
- // Don't copy log messages to tombstone unless this is a development device.
- Tombstone tombstone;
- engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files);
-
- if (proto_fd != -1) {
- if (!tombstone.SerializeToFileDescriptor(proto_fd.get())) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to write proto tombstone: %s",
- strerror(errno));
- }
- }
+ uint64_t abort_msg_address, OpenFilesList* open_files,
+ std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
+ uintptr_t gwp_asan_metadata_ptr) {
+ // don't copy log messages to tombstone unless this is a dev device
+ bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
log_t log;
log.current_tid = target_thread;
@@ -627,45 +619,40 @@ void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::Unw
log.tfd = output_fd.get();
log.amfd_data = amfd_data;
- bool translate_proto = GetBoolProperty("debug.debuggerd.translate_proto_to_text", true);
- if (translate_proto) {
- tombstone_proto_to_text(tombstone, [&log](const std::string& line, bool should_log) {
- _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, "%s\n", line.c_str());
- });
- } else {
- bool want_logs = GetBoolProperty("ro.debuggable", false);
-
- _LOG(&log, logtype::HEADER,
- "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
- dump_header_info(&log);
- _LOG(&log, logtype::HEADER, "Timestamp: %s\n", get_timestamp().c_str());
+ _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+ dump_header_info(&log);
+ dump_timestamp(&log, time(nullptr));
- auto it = threads.find(target_thread);
- if (it == threads.end()) {
- async_safe_fatal("failed to find target thread");
- }
+ auto it = threads.find(target_thread);
+ if (it == threads.end()) {
+ LOG(FATAL) << "failed to find target thread";
+ }
- dump_thread(&log, unwinder, it->second, process_info, true);
+ GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
+ gwp_asan_state_ptr,
+ gwp_asan_metadata_ptr, it->second);
- if (want_logs) {
- dump_logs(&log, it->second.pid, 50);
- }
+ dump_thread(&log, unwinder, it->second, abort_msg_address, true,
+ gwp_asan_crash_data);
- for (auto& [tid, thread_info] : threads) {
- if (tid == target_thread) {
- continue;
- }
+ if (want_logs) {
+ dump_logs(&log, it->second.pid, 50);
+ }
- dump_thread(&log, unwinder, thread_info, process_info, false);
+ for (auto& [tid, thread_info] : threads) {
+ if (tid == target_thread) {
+ continue;
}
- if (open_files) {
- _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n");
- dump_open_files_list(&log, *open_files, " ");
- }
+ dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
+ }
- if (want_logs) {
- dump_logs(&log, it->second.pid, 0);
- }
+ if (open_files) {
+ _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n");
+ dump_open_files_list(&log, *open_files, " ");
+ }
+
+ if (want_logs) {
+ dump_logs(&log, it->second.pid, 0);
}
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
deleted file mode 100644
index ff12017f7..000000000
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/tombstone.h"
-#include "libdebuggerd/gwp_asan.h"
-#include "libdebuggerd/scudo.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <time.h>
-
-#include <memory>
-#include <optional>
-#include <string>
-
-#include <async_safe/log.h>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-
-#include <android/log.h>
-#include <bionic/macros.h>
-#include <log/log.h>
-#include <log/log_read.h>
-#include <log/logprint.h>
-#include <private/android_filesystem_config.h>
-
-#include <procinfo/process.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
-
-#include "libdebuggerd/open_files_list.h"
-#include "libdebuggerd/utility.h"
-#include "util.h"
-
-#include "tombstone.pb.h"
-
-using android::base::StringPrintf;
-
-// Use the demangler from libc++.
-extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
-
-static Architecture get_arch() {
-#if defined(__arm__)
- return Architecture::ARM32;
-#elif defined(__aarch64__)
- return Architecture::ARM64;
-#elif defined(__i386__)
- return Architecture::X86;
-#elif defined(__x86_64__)
- return Architecture::X86_64;
-#else
-#error Unknown architecture!
-#endif
-}
-
-static std::optional<std::string> get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
- unwindstack::Maps* maps) {
- static constexpr uint64_t kMaxDifferenceBytes = 256;
- uint64_t difference;
- if (sp >= fault_addr) {
- difference = sp - fault_addr;
- } else {
- difference = fault_addr - sp;
- }
- if (difference <= kMaxDifferenceBytes) {
- // The faulting address is close to the current sp, check if the sp
- // indicates a stack overflow.
- // On arm, the sp does not get updated when the instruction faults.
- // In this case, the sp will still be in a valid map, which is the
- // last case below.
- // On aarch64, the sp does get updated when the instruction faults.
- // In this case, the sp will be in either an invalid map if triggered
- // on the main thread, or in a guard map if in another thread, which
- // will be the first case or second case from below.
- unwindstack::MapInfo* map_info = maps->Find(sp);
- if (map_info == nullptr) {
- return "stack pointer is in a non-existent map; likely due to stack overflow.";
- } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
- return "stack pointer is not in a rw map; likely due to stack overflow.";
- } else if ((sp - map_info->start()) <= kMaxDifferenceBytes) {
- return "stack pointer is close to top of stack; likely stack overflow.";
- }
- }
- return {};
-}
-
-void set_human_readable_cause(Cause* cause, uint64_t fault_addr) {
- if (!cause->has_memory_error() || !cause->memory_error().has_heap()) {
- return;
- }
-
- const MemoryError& memory_error = cause->memory_error();
- const HeapObject& heap_object = memory_error.heap();
-
- const char *tool_str;
- switch (memory_error.tool()) {
- case MemoryError_Tool_GWP_ASAN:
- tool_str = "GWP-ASan";
- break;
- case MemoryError_Tool_SCUDO:
- tool_str = "MTE";
- break;
- default:
- tool_str = "Unknown";
- break;
- }
-
- const char *error_type_str;
- switch (memory_error.type()) {
- case MemoryError_Type_USE_AFTER_FREE:
- error_type_str = "Use After Free";
- break;
- case MemoryError_Type_DOUBLE_FREE:
- error_type_str = "Double Free";
- break;
- case MemoryError_Type_INVALID_FREE:
- error_type_str = "Invalid (Wild) Free";
- break;
- case MemoryError_Type_BUFFER_OVERFLOW:
- error_type_str = "Buffer Overflow";
- break;
- case MemoryError_Type_BUFFER_UNDERFLOW:
- error_type_str = "Buffer Underflow";
- break;
- default:
- cause->set_human_readable(
- StringPrintf("[%s]: Unknown error occurred at 0x%" PRIx64 ".", tool_str, fault_addr));
- return;
- }
-
- uint64_t diff;
- const char* location_str;
-
- if (fault_addr < heap_object.address()) {
- // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
- location_str = "left of";
- diff = heap_object.address() - fault_addr;
- } else if (fault_addr - heap_object.address() < heap_object.size()) {
- // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
- location_str = "into";
- diff = fault_addr - heap_object.address();
- } else {
- // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
- location_str = "right of";
- diff = fault_addr - heap_object.address() - heap_object.size();
- }
-
- // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
- const char* byte_suffix = "s";
- if (diff == 1) {
- byte_suffix = "";
- }
-
- cause->set_human_readable(StringPrintf(
- "[%s]: %s, %" PRIu64 " byte%s %s a %" PRIu64 "-byte allocation at 0x%" PRIx64, tool_str,
- error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
-}
-
-static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ProcessInfo& process_info, const ThreadInfo& main_thread) {
- ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
- if (scudo_crash_data.CrashIsMine()) {
- scudo_crash_data.AddCauseProtos(tombstone, unwinder);
- return;
- }
-
- GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
- main_thread);
- if (gwp_asan_crash_data.CrashIsMine()) {
- gwp_asan_crash_data.AddCauseProtos(tombstone, unwinder);
- return;
- }
-
- const siginfo *si = main_thread.siginfo;
- auto fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
- unwindstack::Maps* maps = unwinder->GetMaps();
-
- std::optional<std::string> cause;
- if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
- if (fault_addr < 4096) {
- cause = "null pointer dereference";
- } else if (fault_addr == 0xffff0ffc) {
- cause = "call to kuser_helper_version";
- } else if (fault_addr == 0xffff0fe0) {
- cause = "call to kuser_get_tls";
- } else if (fault_addr == 0xffff0fc0) {
- cause = "call to kuser_cmpxchg";
- } else if (fault_addr == 0xffff0fa0) {
- cause = "call to kuser_memory_barrier";
- } else if (fault_addr == 0xffff0f60) {
- cause = "call to kuser_cmpxchg64";
- } else {
- cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
- }
- } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
- unwindstack::MapInfo* map_info = maps->Find(fault_addr);
- if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
- cause = "execute-only (no-read) memory access error; likely due to data in .text.";
- } else {
- cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
- }
- } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
- cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
- si->si_syscall);
- }
-
- if (cause) {
- Cause *cause_proto = tombstone->add_causes();
- cause_proto->set_human_readable(*cause);
- }
-}
-
-static void dump_abort_message(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ProcessInfo& process_info) {
- std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
- uintptr_t address = process_info.abort_msg_address;
- if (address == 0) {
- return;
- }
-
- size_t length;
- if (!process_memory->ReadFully(address, &length, sizeof(length))) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read abort message header: %s",
- strerror(errno));
- return;
- }
-
- // The length field includes the length of the length field itself.
- if (length < sizeof(size_t)) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
- "abort message header malformed: claimed length = %zu", length);
- return;
- }
-
- length -= sizeof(size_t);
-
- // The abort message should be null terminated already, but reserve a spot for NUL just in case.
- std::string msg;
- msg.resize(length);
-
- if (!process_memory->ReadFully(address + sizeof(length), &msg[0], length)) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read abort message header: %s",
- strerror(errno));
- return;
- }
-
- tombstone->set_abort_message(msg);
-}
-
-static void dump_open_fds(Tombstone* tombstone, const OpenFilesList* open_files) {
- if (open_files) {
- for (auto& [fd, entry] : *open_files) {
- FD f;
-
- f.set_fd(fd);
-
- const std::optional<std::string>& path = entry.path;
- if (path) {
- f.set_path(*path);
- }
-
- const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;
- if (fdsan_owner) {
- const char* type = android_fdsan_get_tag_type(*fdsan_owner);
- uint64_t value = android_fdsan_get_tag_value(*fdsan_owner);
- f.set_owner(type);
- f.set_tag(value);
- }
-
- *tombstone->add_open_fds() = f;
- }
- }
-}
-
-void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
- unwindstack::Maps* maps) {
- f->set_rel_pc(frame.rel_pc);
- f->set_pc(frame.pc);
- f->set_sp(frame.sp);
-
- if (!frame.function_name.empty()) {
- // TODO: Should this happen here, or on the display side?
- char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
- if (demangled_name) {
- f->set_function_name(demangled_name);
- free(demangled_name);
- } else {
- f->set_function_name(frame.function_name);
- }
- }
-
- f->set_function_offset(frame.function_offset);
-
- if (frame.map_start == frame.map_end) {
- // No valid map associated with this frame.
- f->set_file_name("<unknown>");
- } else if (!frame.map_name.empty()) {
- f->set_file_name(frame.map_name);
- } else {
- f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start));
- }
-
- f->set_file_map_offset(frame.map_elf_start_offset);
-
- unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
- if (map_info) {
- f->set_build_id(map_info->GetPrintableBuildID());
- }
-}
-
-static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ThreadInfo& thread_info, bool memory_dump = false) {
- Thread thread;
-
- thread.set_id(thread_info.tid);
- thread.set_name(thread_info.thread_name);
- thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
-
- unwindstack::Maps* maps = unwinder->GetMaps();
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
- thread_info.registers->IterateRegisters(
- [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
- Register r;
- r.set_name(name);
- r.set_u64(value);
- *thread.add_registers() = r;
-
- if (memory_dump) {
- MemoryDump dump;
-
- dump.set_register_name(name);
- unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
- if (map_info) {
- dump.set_mapping_name(map_info->name());
- }
-
- constexpr size_t kNumBytesAroundRegister = 256;
- constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
- char buf[kNumBytesAroundRegister];
- uint8_t tags[kNumTagsAroundRegister];
- size_t start_offset = 0;
- ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
- if (bytes == -1) {
- return;
- }
- dump.set_begin_address(value);
-
- if (start_offset + bytes > sizeof(buf)) {
- async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
- start_offset, bytes);
- }
-
- dump.set_memory(buf, bytes);
-
- bool has_tags = false;
-#if defined(__aarch64__)
- for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
- if (tags[i] != 0) {
- has_tags = true;
- }
- }
-#endif // defined(__aarch64__)
-
- if (has_tags) {
- dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
- }
-
- *thread.add_memory_dump() = std::move(dump);
- }
- });
-
- std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
- unwinder->SetRegs(regs_copy.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
- unwinder->LastErrorCodeString());
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
- unwinder->LastErrorAddress());
- }
- } else {
- if (unwinder->elf_from_memory_not_file()) {
- auto backtrace_note = thread.mutable_backtrace_note();
- *backtrace_note->Add() =
- "Function names and BuildId information is missing for some frames due";
- *backtrace_note->Add() =
- "to unreadable libraries. For unwinds of apps, only shared libraries";
- *backtrace_note->Add() = "found under the lib/ directory are readable.";
- *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
- }
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
- BacktraceFrame* f = thread.add_current_backtrace();
- fill_in_backtrace_frame(f, frame, maps);
- }
- }
-
- auto& threads = *tombstone->mutable_threads();
- threads[thread_info.tid] = thread;
-}
-
-static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ThreadInfo& thread_info) {
- dump_thread(tombstone, unwinder, thread_info, true);
-}
-
-static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
- unwindstack::Maps* maps = unwinder->GetMaps();
- std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
-
- for (const auto& map_info : *maps) {
- auto* map = tombstone->add_memory_mappings();
- map->set_begin_address(map_info->start());
- map->set_end_address(map_info->end());
- map->set_offset(map_info->offset());
-
- if (map_info->flags() & PROT_READ) {
- map->set_read(true);
- }
- if (map_info->flags() & PROT_WRITE) {
- map->set_write(true);
- }
- if (map_info->flags() & PROT_EXEC) {
- map->set_execute(true);
- }
-
- map->set_mapping_name(map_info->name());
-
- std::string build_id = map_info->GetPrintableBuildID();
- if (!build_id.empty()) {
- map->set_build_id(build_id);
- }
-
- map->set_load_bias(map_info->GetLoadBias(process_memory));
- }
-}
-
-static void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) {
- logger_list* logger_list =
- android_logger_list_open(android_name_to_log_id(logger), ANDROID_LOG_NONBLOCK, 0, pid);
-
- LogBuffer buffer;
-
- while (true) {
- log_msg log_entry;
- ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-
- if (actual < 0) {
- if (actual == -EINTR) {
- // interrupted by signal, retry
- continue;
- }
- if (actual == -EAGAIN) {
- // non-blocking EOF; we're done
- break;
- } else {
- break;
- }
- } else if (actual == 0) {
- break;
- }
-
- char timestamp_secs[32];
- time_t sec = static_cast<time_t>(log_entry.entry.sec);
- tm tm;
- localtime_r(&sec, &tm);
- strftime(timestamp_secs, sizeof(timestamp_secs), "%m-%d %H:%M:%S", &tm);
- std::string timestamp =
- StringPrintf("%s.%03d", timestamp_secs, log_entry.entry.nsec / 1'000'000);
-
- // Msg format is: <priority:1><tag:N>\0<message:N>\0
- char* msg = log_entry.msg();
- if (msg == nullptr) {
- continue;
- }
-
- unsigned char prio = msg[0];
- char* tag = msg + 1;
- msg = tag + strlen(tag) + 1;
-
- // consume any trailing newlines
- char* nl = msg + strlen(msg) - 1;
- while (nl >= msg && *nl == '\n') {
- *nl-- = '\0';
- }
-
- // Look for line breaks ('\n') and display each text line
- // on a separate line, prefixed with the header, like logcat does.
- do {
- nl = strchr(msg, '\n');
- if (nl != nullptr) {
- *nl = '\0';
- ++nl;
- }
-
- LogMessage* log_msg = buffer.add_logs();
- log_msg->set_timestamp(timestamp);
- log_msg->set_pid(log_entry.entry.pid);
- log_msg->set_tid(log_entry.entry.tid);
- log_msg->set_priority(prio);
- log_msg->set_tag(tag);
- log_msg->set_message(msg);
- } while ((msg = nl));
- }
- android_logger_list_free(logger_list);
-
- if (!buffer.logs().empty()) {
- buffer.set_name(logger);
- *tombstone->add_log_buffers() = std::move(buffer);
- }
-}
-
-static void dump_logcat(Tombstone* tombstone, pid_t pid) {
- dump_log_file(tombstone, "system", pid);
- dump_log_file(tombstone, "main", pid);
-}
-
-static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
- unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
- if (tombstone.arch() != Architecture::ARM64) return;
-
- fault_addr = untag_address(fault_addr);
- constexpr size_t kNumGranules = kNumTagRows * kNumTagColumns;
- constexpr size_t kBytesToRead = kNumGranules * kTagGranuleSize;
-
- // If the low part of the tag dump would underflow to the high address space, it's probably not
- // a valid address for us to dump tags from.
- if (fault_addr < kBytesToRead / 2) return;
-
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
- constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
- size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
- MemoryDump tag_dump;
- size_t granules_to_read = kNumGranules;
-
- // Attempt to read the first tag. If reading fails, this likely indicates the
- // lowest touched page is inaccessible or not marked with PROT_MTE.
- // Fast-forward over pages until one has tags, or we exhaust the search range.
- while (memory->ReadTag(start_address) < 0) {
- size_t page_size = sysconf(_SC_PAGE_SIZE);
- size_t bytes_to_next_page = page_size - (start_address % page_size);
- if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
- start_address += bytes_to_next_page;
- granules_to_read -= bytes_to_next_page / kTagGranuleSize;
- }
- tag_dump.set_begin_address(start_address);
-
- std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
-
- for (size_t i = 0; i < granules_to_read; ++i) {
- long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
- if (tag < 0) break;
- mte_tags->push_back(static_cast<uint8_t>(tag));
- }
-
- if (!mte_tags->empty()) {
- *signal->mutable_fault_adjacent_metadata() = tag_dump;
- }
-}
-
-static std::optional<uint64_t> read_uptime_secs() {
- std::string uptime;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
- return {};
- }
- return strtoll(uptime.c_str(), nullptr, 10);
-}
-
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
- const ProcessInfo& process_info, const OpenFilesList* open_files) {
- Tombstone result;
-
- result.set_arch(get_arch());
- result.set_build_fingerprint(android::base::GetProperty("ro.build.fingerprint", "unknown"));
- result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
- result.set_timestamp(get_timestamp());
-
- std::optional<uint64_t> system_uptime = read_uptime_secs();
- if (system_uptime) {
- android::procinfo::ProcessInfo proc_info;
- std::string error;
- if (android::procinfo::GetProcessInfo(target_thread, &proc_info, &error)) {
- uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
- result.set_process_uptime(*system_uptime - starttime);
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read process info: %s",
- error.c_str());
- }
- } else {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read /proc/uptime: %s",
- strerror(errno));
- }
-
- const ThreadInfo& main_thread = threads.at(target_thread);
- result.set_pid(main_thread.pid);
- result.set_tid(main_thread.tid);
- result.set_uid(main_thread.uid);
- result.set_selinux_label(main_thread.selinux_label);
-
- auto cmd_line = result.mutable_command_line();
- for (const auto& arg : main_thread.command_line) {
- *cmd_line->Add() = arg;
- }
-
- if (!main_thread.siginfo) {
- async_safe_fatal("siginfo missing");
- }
-
- Signal sig;
- sig.set_number(main_thread.signo);
- sig.set_name(get_signame(main_thread.siginfo));
- sig.set_code(main_thread.siginfo->si_code);
- sig.set_code_name(get_sigcode(main_thread.siginfo));
-
- if (signal_has_sender(main_thread.siginfo, main_thread.pid)) {
- sig.set_has_sender(true);
- sig.set_sender_uid(main_thread.siginfo->si_uid);
- sig.set_sender_pid(main_thread.siginfo->si_pid);
- }
-
- if (process_info.has_fault_address) {
- sig.set_has_fault_address(true);
- uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
- sig.set_fault_address(fault_addr);
- dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
- }
-
- *result.mutable_signal_info() = sig;
-
- dump_abort_message(&result, unwinder, process_info);
-
- dump_main_thread(&result, unwinder, main_thread);
-
- for (const auto& [tid, thread_info] : threads) {
- if (tid != target_thread) {
- dump_thread(&result, unwinder, thread_info);
- }
- }
-
- dump_probable_cause(&result, unwinder, process_info, main_thread);
-
- dump_mappings(&result, unwinder);
-
- // Only dump logs on debuggable devices.
- if (android::base::GetBoolProperty("ro.debuggable", false)) {
- dump_logcat(&result, main_thread.pid);
- }
-
- dump_open_fds(&result, open_files);
-
- *tombstone = std::move(result);
-}
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
deleted file mode 100644
index 053299a81..000000000
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libdebuggerd/tombstone.h>
-
-#include <inttypes.h>
-
-#include <functional>
-#include <set>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <async_safe/log.h>
-#include <bionic/macros.h>
-
-#include "tombstone.pb.h"
-
-using android::base::StringAppendF;
-using android::base::StringPrintf;
-
-#define CB(log, ...) callback(StringPrintf(__VA_ARGS__), log)
-#define CBL(...) CB(true, __VA_ARGS__)
-#define CBS(...) CB(false, __VA_ARGS__)
-using CallbackType = std::function<void(const std::string& line, bool should_log)>;
-
-static const char* abi_string(const Tombstone& tombstone) {
- switch (tombstone.arch()) {
- case Architecture::ARM32:
- return "arm";
- case Architecture::ARM64:
- return "arm64";
- case Architecture::X86:
- return "x86";
- case Architecture::X86_64:
- return "x86_64";
- default:
- return "<unknown>";
- }
-}
-
-static int pointer_width(const Tombstone& tombstone) {
- switch (tombstone.arch()) {
- case Architecture::ARM32:
- return 4;
- case Architecture::ARM64:
- return 8;
- case Architecture::X86:
- return 4;
- case Architecture::X86_64:
- return 8;
- default:
- return 8;
- }
-}
-
-static void print_thread_header(CallbackType callback, const Tombstone& tombstone,
- const Thread& thread, bool should_log) {
- const char* process_name = "<unknown>";
- if (!tombstone.command_line().empty()) {
- process_name = tombstone.command_line()[0].c_str();
- CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
- }
- CB(should_log, "pid: %d, tid: %d, name: %s >>> %s <<<", tombstone.pid(), thread.id(),
- thread.name().c_str(), process_name);
- CB(should_log, "uid: %d", tombstone.uid());
- if (thread.tagged_addr_ctrl() != -1) {
- CB(should_log, "tagged_addr_ctrl: %016" PRIx64, thread.tagged_addr_ctrl());
- }
-}
-
-static void print_register_row(CallbackType callback, int word_size,
- std::vector<std::pair<std::string, uint64_t>> row, bool should_log) {
- std::string output = " ";
- for (const auto& [name, value] : row) {
- output += android::base::StringPrintf(" %-3s %0*" PRIx64, name.c_str(), 2 * word_size,
- static_cast<uint64_t>(value));
- }
- callback(output, should_log);
-}
-
-static void print_thread_registers(CallbackType callback, const Tombstone& tombstone,
- const Thread& thread, bool should_log) {
- static constexpr size_t column_count = 4;
- std::vector<std::pair<std::string, uint64_t>> current_row;
- std::vector<std::pair<std::string, uint64_t>> special_row;
- std::unordered_set<std::string> special_registers;
-
- int word_size = pointer_width(tombstone);
-
- switch (tombstone.arch()) {
- case Architecture::ARM32:
- special_registers = {"ip", "lr", "sp", "pc", "pst"};
- break;
-
- case Architecture::ARM64:
- special_registers = {"ip", "lr", "sp", "pc", "pst"};
- break;
-
- case Architecture::X86:
- special_registers = {"ebp", "esp", "eip"};
- break;
-
- case Architecture::X86_64:
- special_registers = {"rbp", "rsp", "rip"};
- break;
-
- default:
- async_safe_fatal("unknown architecture");
- }
-
- for (const auto& reg : thread.registers()) {
- auto row = &current_row;
- if (special_registers.count(reg.name()) == 1) {
- row = &special_row;
- }
-
- row->emplace_back(reg.name(), reg.u64());
- if (current_row.size() == column_count) {
- print_register_row(callback, word_size, current_row, should_log);
- current_row.clear();
- }
- }
-
- if (!current_row.empty()) {
- print_register_row(callback, word_size, current_row, should_log);
- }
-
- print_register_row(callback, word_size, special_row, should_log);
-}
-
-static void print_backtrace(CallbackType callback, const Tombstone& tombstone,
- const google::protobuf::RepeatedPtrField<BacktraceFrame>& backtrace,
- bool should_log) {
- int index = 0;
- for (const auto& frame : backtrace) {
- std::string function;
-
- if (!frame.function_name().empty()) {
- function =
- StringPrintf(" (%s+%" PRId64 ")", frame.function_name().c_str(), frame.function_offset());
- }
-
- std::string build_id;
- if (!frame.build_id().empty()) {
- build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
- }
-
- CB(should_log, " #%02d pc %0*" PRIx64 " %s%s%s", index++, pointer_width(tombstone) * 2,
- frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
- }
-}
-
-static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone,
- const Thread& thread, bool should_log) {
- CBS("");
- CB(should_log, "backtrace:");
- if (!thread.backtrace_note().empty()) {
- CB(should_log, " NOTE: %s",
- android::base::Join(thread.backtrace_note(), "\n NOTE: ").c_str());
- }
- print_backtrace(callback, tombstone, thread.current_backtrace(), should_log);
-}
-
-static void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone,
- const Thread& thread) {
- static constexpr size_t bytes_per_line = 16;
- static_assert(bytes_per_line == kTagGranuleSize);
- int word_size = pointer_width(tombstone);
- for (const auto& mem : thread.memory_dump()) {
- CBS("");
- if (mem.mapping_name().empty()) {
- CBS("memory near %s:", mem.register_name().c_str());
- } else {
- CBS("memory near %s (%s):", mem.register_name().c_str(), mem.mapping_name().c_str());
- }
- uint64_t addr = mem.begin_address();
- for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
- uint64_t tagged_addr = addr;
- if (mem.has_arm_mte_metadata() &&
- mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
- tagged_addr |=
- static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
- << 56;
- }
- std::string line = StringPrintf(" %0*" PRIx64, word_size * 2, tagged_addr + offset);
-
- size_t bytes = std::min(bytes_per_line, mem.memory().size() - offset);
- for (size_t i = 0; i < bytes; i += word_size) {
- uint64_t word = 0;
-
- // Assumes little-endian, but what doesn't?
- memcpy(&word, mem.memory().data() + offset + i, word_size);
-
- StringAppendF(&line, " %0*" PRIx64, word_size * 2, word);
- }
-
- char ascii[bytes_per_line + 1];
-
- memset(ascii, '.', sizeof(ascii));
- ascii[bytes_per_line] = '\0';
-
- for (size_t i = 0; i < bytes; ++i) {
- uint8_t byte = mem.memory()[offset + i];
- if (byte >= 0x20 && byte < 0x7f) {
- ascii[i] = byte;
- }
- }
-
- CBS("%s %s", line.c_str(), ascii);
- }
- }
-}
-
-static void print_thread(CallbackType callback, const Tombstone& tombstone, const Thread& thread) {
- print_thread_header(callback, tombstone, thread, false);
- print_thread_registers(callback, tombstone, thread, false);
- print_thread_backtrace(callback, tombstone, thread, false);
- print_thread_memory_dump(callback, tombstone, thread);
-}
-
-static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
- if (!tombstone.has_signal_info()) return;
-
- const Signal& signal = tombstone.signal_info();
-
- if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
- return;
- }
-
- const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
-
- if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
- return;
- }
-
- const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
-
- CBS("");
- CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
- signal.fault_address(), kTagGranuleSize);
- constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
-
- size_t tag_index = 0;
- size_t num_tags = tags.length();
- uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1);
- for (size_t row = 0; tag_index < num_tags; ++row) {
- uintptr_t row_addr =
- (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
- std::string row_contents;
- bool row_has_fault = false;
-
- for (size_t column = 0; column < kNumTagColumns; ++column) {
- uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
- if (granule_addr < memory_dump.begin_address() ||
- granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
- row_contents += " . ";
- } else if (granule_addr == fault_granule) {
- row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
- row_has_fault = true;
- } else {
- row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
- }
- }
-
- if (row_contents.back() == ' ') row_contents.pop_back();
-
- if (row_has_fault) {
- CBS(" =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
- } else {
- CBS(" 0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
- }
- }
-}
-
-static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
- const Thread& thread) {
- print_thread_header(callback, tombstone, thread, true);
-
- const Signal& signal_info = tombstone.signal_info();
- std::string sender_desc;
-
- if (signal_info.has_sender()) {
- sender_desc =
- StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
- }
-
- if (!tombstone.has_signal_info()) {
- CBL("signal information missing");
- } else {
- std::string fault_addr_desc;
- if (signal_info.has_fault_address()) {
- fault_addr_desc = StringPrintf("0x%" PRIx64, signal_info.fault_address());
- } else {
- fault_addr_desc = "--------";
- }
-
- CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
- signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
- sender_desc.c_str(), fault_addr_desc.c_str());
- }
-
- if (tombstone.causes_size() == 1) {
- CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
- }
-
- if (!tombstone.abort_message().empty()) {
- CBL("Abort message: '%s'", tombstone.abort_message().c_str());
- }
-
- print_thread_registers(callback, tombstone, thread, true);
- print_thread_backtrace(callback, tombstone, thread, true);
-
- if (tombstone.causes_size() > 1) {
- CBS("");
- CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
- "order of probability.");
- }
-
- for (const Cause& cause : tombstone.causes()) {
- if (tombstone.causes_size() > 1) {
- CBS("");
- CBL("Cause: %s", cause.human_readable().c_str());
- }
-
- if (cause.has_memory_error() && cause.memory_error().has_heap()) {
- const HeapObject& heap_object = cause.memory_error().heap();
-
- if (heap_object.deallocation_backtrace_size() != 0) {
- CBS("");
- CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
- print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
- }
-
- if (heap_object.allocation_backtrace_size() != 0) {
- CBS("");
- CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
- print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
- }
- }
- }
-
- print_tag_dump(callback, tombstone);
-
- print_thread_memory_dump(callback, tombstone, thread);
-
- CBS("");
- CBS("memory map (%d %s):", tombstone.memory_mappings().size(),
- tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
- int word_size = pointer_width(tombstone);
- const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
- if (word_size == 8) {
- uint64_t top = ptr >> 32;
- uint64_t bottom = ptr & 0xFFFFFFFF;
- return StringPrintf("%08" PRIx64 "'%08" PRIx64, top, bottom);
- }
-
- return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
- };
-
- for (const auto& map : tombstone.memory_mappings()) {
- std::string line = " ";
- StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
- format_pointer(map.end_address() - 1).c_str());
- StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
- map.execute() ? "x" : "-");
- StringAppendF(&line, " %8" PRIx64 " %8" PRIx64, map.offset(),
- map.end_address() - map.begin_address());
-
- if (!map.mapping_name().empty()) {
- StringAppendF(&line, " %s", map.mapping_name().c_str());
-
- if (!map.build_id().empty()) {
- StringAppendF(&line, " (BuildId: %s)", map.build_id().c_str());
- }
-
- if (map.load_bias() != 0) {
- StringAppendF(&line, " (load bias 0x%" PRIx64 ")", map.load_bias());
- }
- }
-
- CBS("%s", line.c_str());
- }
-}
-
-void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
- for (const auto& buffer : tombstone.log_buffers()) {
- if (tail) {
- CBS("--------- tail end of log %s", buffer.name().c_str());
- } else {
- CBS("--------- log %s", buffer.name().c_str());
- }
-
- int begin = 0;
- if (tail != 0) {
- begin = std::max(0, buffer.logs().size() - tail);
- }
-
- for (int i = begin; i < buffer.logs().size(); ++i) {
- const LogMessage& msg = buffer.logs(i);
-
- static const char* kPrioChars = "!.VDIWEFS";
- char priority = (msg.priority() < strlen(kPrioChars) ? kPrioChars[msg.priority()] : '?');
- CBS("%s %5u %5u %c %-8s: %s", msg.timestamp().c_str(), msg.pid(), msg.tid(), priority,
- msg.tag().c_str(), msg.message().c_str());
- }
- }
-}
-
-bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) {
- CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
- CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str());
- CBL("Revision: '%s'", tombstone.revision().c_str());
- CBL("ABI: '%s'", abi_string(tombstone));
- CBL("Timestamp: %s", tombstone.timestamp().c_str());
- CBL("Process uptime: %ds", tombstone.process_uptime());
-
- // Process header
- const auto& threads = tombstone.threads();
- auto main_thread_it = threads.find(tombstone.tid());
- if (main_thread_it == threads.end()) {
- CBL("failed to find entry for main thread in tombstone");
- return false;
- }
-
- const auto& main_thread = main_thread_it->second;
-
- print_main_thread(callback, tombstone, main_thread);
-
- print_logs(callback, tombstone, 50);
-
- // protobuf's map is unordered, so sort the keys first.
- std::set<int> thread_ids;
- for (const auto& [tid, _] : threads) {
- if (tid != tombstone.tid()) {
- thread_ids.insert(tid);
- }
- }
-
- for (const auto& tid : thread_ids) {
- CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
- print_thread(callback, tombstone, threads.find(tid)->second);
- }
-
- if (tombstone.open_fds().size() > 0) {
- CBS("");
- CBS("open files:");
- for (const auto& fd : tombstone.open_fds()) {
- std::optional<std::string> owner;
- if (!fd.owner().empty()) {
- owner = StringPrintf("owned by %s 0x%" PRIx64, fd.owner().c_str(), fd.tag());
- }
-
- CBS(" fd %d: %s (%s)", fd.fd(), fd.path().c_str(), owner ? owner->c_str() : "unowned");
- }
- }
-
- print_logs(callback, tombstone, 0);
-
- return true;
-}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 2c645b542..0a1d2a4da 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -30,11 +30,11 @@
#include <string>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
@@ -43,6 +43,7 @@
using android::base::unique_fd;
+// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
if ((ltype == HEADER)
|| (ltype == REGISTERS)
@@ -125,57 +126,58 @@ void _VLOG(log_t* log, enum logtype ltype, const char* fmt, va_list ap) {
#define MEMORY_BYTES_TO_DUMP 256
#define MEMORY_BYTES_PER_LINE 16
-static_assert(MEMORY_BYTES_PER_LINE == kTagGranuleSize);
-
-ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,
- unwindstack::Memory* memory) {
- // Align the address to the number of bytes per line to avoid confusing memory tag output if
- // memory is tagged and we start from a misaligned address. Start 32 bytes before the address.
- *addr &= ~(MEMORY_BYTES_PER_LINE - 1);
- if (*addr >= 4128) {
- *addr -= 32;
- }
- // We don't want the address tag to appear in the addresses in the memory dump.
- *addr = untag_address(*addr);
+void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
+ // Align the address to sizeof(long) and start 32 bytes before the address.
+ addr &= ~(sizeof(long) - 1);
+ if (addr >= 4128) {
+ addr -= 32;
+ }
- // Don't bother if the address would overflow, taking tag bits into account. Note that
- // untag_address truncates to 32 bits on 32-bit platforms as a side effect of returning a
- // uintptr_t, so this also checks for 32-bit overflow.
- if (untag_address(*addr + MEMORY_BYTES_TO_DUMP - 1) < *addr) {
- return -1;
+ // Don't bother if the address looks too low, or looks too high.
+ if (addr < 4096 ||
+#if defined(__LP64__)
+ addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
+#else
+ addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
+#endif
+ return;
}
- memset(out, 0, len);
+ _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
- size_t bytes = memory->Read(*addr, reinterpret_cast<uint8_t*>(out), len);
+ // Dump 256 bytes
+ uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
+ memset(data, 0, MEMORY_BYTES_TO_DUMP);
+ size_t bytes = memory->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
if (bytes % sizeof(uintptr_t) != 0) {
// This should never happen, but just in case.
ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
bytes &= ~(sizeof(uintptr_t) - 1);
}
+ uint64_t start = 0;
bool skip_2nd_read = false;
if (bytes == 0) {
// In this case, we might want to try another read at the beginning of
// the next page only if it's within the amount of memory we would have
// read.
size_t page_size = sysconf(_SC_PAGE_SIZE);
- uint64_t next_page = (*addr + (page_size - 1)) & ~(page_size - 1);
- if (next_page == *addr || next_page >= *addr + len) {
+ start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+ if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
skip_2nd_read = true;
}
- *addr = next_page;
}
- if (bytes < len && !skip_2nd_read) {
+ if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
// Try to do one more read. This could happen if a read crosses a map,
// but the maps do not have any break between them. Or it could happen
// if reading from an unreadable map, but the read would cross back
// into a readable map. Only requires one extra read because a map has
// to contain at least one page, and the total number of bytes to dump
// is smaller than a page.
- size_t bytes2 = memory->Read(*addr + bytes, static_cast<uint8_t*>(out) + bytes, len - bytes);
+ size_t bytes2 = memory->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+ sizeof(data) - bytes - start);
bytes += bytes2;
if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
// This should never happen, but we'll try and continue any way.
@@ -184,36 +186,6 @@ ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint6
}
}
- // If we were unable to read anything, it probably means that the register doesn't contain a
- // valid pointer.
- if (bytes == 0) {
- return -1;
- }
-
- for (uint64_t tag_granule = 0; tag_granule < bytes / kTagGranuleSize; ++tag_granule) {
- long tag = memory->ReadTag(*addr + kTagGranuleSize * tag_granule);
- if (tag_granule < tags_len) {
- tags[tag_granule] = tag >= 0 ? tag : 0;
- } else {
- ALOGE("Insufficient space for tags");
- }
- }
-
- return bytes;
-}
-
-void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
- // Dump 256 bytes
- uintptr_t data[MEMORY_BYTES_TO_DUMP / sizeof(uintptr_t)];
- uint8_t tags[MEMORY_BYTES_TO_DUMP / kTagGranuleSize];
-
- ssize_t bytes = dump_memory(data, sizeof(data), tags, sizeof(tags), &addr, memory);
- if (bytes == -1) {
- return;
- }
-
- _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
-
// Dump the code around memory as:
// addr contents ascii
// 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
@@ -221,32 +193,55 @@ void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const s
// On 32-bit machines, there are still 16 bytes per line but addresses and
// words are of course presented differently.
uintptr_t* data_ptr = data;
- uint8_t* tags_ptr = tags;
- for (size_t line = 0; line < static_cast<size_t>(bytes) / MEMORY_BYTES_PER_LINE; line++) {
- uint64_t tagged_addr = addr | static_cast<uint64_t>(*tags_ptr++) << 56;
+ size_t current = 0;
+ size_t total_bytes = start + bytes;
+ for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
std::string logline;
- android::base::StringAppendF(&logline, " %" PRIPTR, tagged_addr);
+ android::base::StringAppendF(&logline, " %" PRIPTR, addr);
addr += MEMORY_BYTES_PER_LINE;
std::string ascii;
for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
- android::base::StringAppendF(&logline, " %" PRIPTR, static_cast<uint64_t>(*data_ptr));
-
- // Fill out the ascii string from the data.
- uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
- for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
- if (*ptr >= 0x20 && *ptr < 0x7f) {
- ascii += *ptr;
- } else {
- ascii += '.';
+ if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
+ android::base::StringAppendF(&logline, " %" PRIPTR, static_cast<uint64_t>(*data_ptr));
+
+ // Fill out the ascii string from the data.
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
+ for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
+ if (*ptr >= 0x20 && *ptr < 0x7f) {
+ ascii += *ptr;
+ } else {
+ ascii += '.';
+ }
}
+ data_ptr++;
+ } else {
+ logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
+ ascii += std::string(sizeof(uintptr_t), '.');
}
- data_ptr++;
+ current += sizeof(uintptr_t);
}
_LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str());
}
}
+void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
+ unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
+ if (fd != -1) {
+ int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
+ if (rc != -1) {
+ buf[rc] = '\0';
+
+ // Trim trailing newlines.
+ if (rc > 0 && buf[rc - 1] == '\n') {
+ buf[rc - 1] = '\0';
+ }
+ return;
+ }
+ }
+ strcpy(buf, default_value);
+}
+
void drop_capabilities() {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
@@ -257,11 +252,11 @@ void drop_capabilities() {
memset(&capdata, 0, sizeof(capdata));
if (capset(&capheader, &capdata[0]) == -1) {
- async_safe_fatal("failed to drop capabilities: %s", strerror(errno));
+ PLOG(FATAL) << "failed to drop capabilities";
}
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
- async_safe_fatal("failed to set PR_SET_NO_NEW_PRIVS: %s", strerror(errno));
+ PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
}
}
@@ -379,20 +374,14 @@ const char* get_sigcode(const siginfo_t* si) {
return "SEGV_ADIDERR";
case SEGV_ADIPERR:
return "SEGV_ADIPERR";
- case SEGV_MTEAERR:
- return "SEGV_MTEAERR";
- case SEGV_MTESERR:
- return "SEGV_MTESERR";
}
- static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code");
+ static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
break;
case SIGSYS:
switch (si->si_code) {
case SYS_SECCOMP: return "SYS_SECCOMP";
- case SYS_USER_DISPATCH:
- return "SYS_USER_DISPATCH";
}
- static_assert(NSIGSYS == SYS_USER_DISPATCH, "missing SYS_* si_code");
+ static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
break;
case SIGTRAP:
switch (si->si_code) {
diff --git a/debuggerd/pbtombstone.cpp b/debuggerd/pbtombstone.cpp
deleted file mode 100644
index 7527e31e1..000000000
--- a/debuggerd/pbtombstone.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <err.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <android-base/unique_fd.h>
-#include <libdebuggerd/tombstone.h>
-
-#include "tombstone.pb.h"
-
-using android::base::unique_fd;
-
-[[noreturn]] void usage(bool error) {
- fprintf(stderr, "usage: pbtombstone TOMBSTONE.PB\n");
- fprintf(stderr, "Convert a protobuf tombstone to text.\n");
- exit(error);
-}
-
-int main(int argc, const char* argv[]) {
- if (argc != 2) {
- usage(true);
- }
-
- if (strcmp("-h", argv[1]) == 0 || strcmp("--help", argv[1]) == 0) {
- usage(false);
- }
-
- unique_fd fd(open(argv[1], O_RDONLY | O_CLOEXEC));
- if (fd == -1) {
- err(1, "failed to open tombstone '%s'", argv[1]);
- }
-
- Tombstone tombstone;
- if (!tombstone.ParseFromFileDescriptor(fd.get())) {
- err(1, "failed to parse tombstone");
- }
-
- bool result = tombstone_proto_to_text(
- tombstone, [](const std::string& line, bool) { printf("%s\n", line.c_str()); });
-
- if (!result) {
- errx(1, "tombstone was malformed");
- }
-
- return 0;
-}
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
deleted file mode 100644
index 73cf5737d..000000000
--- a/debuggerd/proto/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "libtombstone_proto-src",
- srcs: ["tombstone.proto"],
-}
-
-cc_library_static {
- name: "libtombstone_proto",
- cflags: [
- "-Wall",
- "-Wextra",
- "-Wthread-safety",
- "-Werror",
- ],
-
- compile_multilib: "both",
-
- proto: {
- export_proto_headers: true,
- type: "lite",
- },
-
- srcs: [":libtombstone_proto-src"],
-
- // b/155341058: Soong doesn't automatically add libprotobuf if there aren't any explicitly
- // listed protos in srcs.
- static_libs: ["libprotobuf-cpp-lite"],
-
- stl: "libc++_static",
- apex_available: [
- "//apex_available:platform",
- "com.android.runtime",
- ],
-
- recovery_available: true,
- vendor_ramdisk_available: true,
-}
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
deleted file mode 100644
index a701212d8..000000000
--- a/debuggerd/proto/tombstone.proto
+++ /dev/null
@@ -1,207 +0,0 @@
-syntax = "proto3";
-
-option java_package = "com.android.server.os";
-option java_outer_classname = "TombstoneProtos";
-
-// NOTE TO OEMS:
-// If you add custom fields to this proto, do not use numbers in the reserved range.
-
-message Tombstone {
- Architecture arch = 1;
- string build_fingerprint = 2;
- string revision = 3;
- string timestamp = 4;
-
- uint32 pid = 5;
- uint32 tid = 6;
- uint32 uid = 7;
- string selinux_label = 8;
-
- repeated string command_line = 9;
-
- // Process uptime in seconds.
- uint32 process_uptime = 20;
-
- Signal signal_info = 10;
- string abort_message = 14;
- repeated Cause causes = 15;
-
- map<uint32, Thread> threads = 16;
- repeated MemoryMapping memory_mappings = 17;
- repeated LogBuffer log_buffers = 18;
- repeated FD open_fds = 19;
-
- reserved 21 to 999;
-}
-
-enum Architecture {
- ARM32 = 0;
- ARM64 = 1;
- X86 = 2;
- X86_64 = 3;
-
- reserved 4 to 999;
-}
-
-message Signal {
- int32 number = 1;
- string name = 2;
-
- int32 code = 3;
- string code_name = 4;
-
- bool has_sender = 5;
- int32 sender_uid = 6;
- int32 sender_pid = 7;
-
- bool has_fault_address = 8;
- uint64 fault_address = 9;
- // Note, may or may not contain the dump of the actual memory contents. Currently, on arm64, we
- // only include metadata, and not the contents.
- MemoryDump fault_adjacent_metadata = 10;
-
- reserved 11 to 999;
-}
-
-message HeapObject {
- uint64 address = 1;
- uint64 size = 2;
-
- uint64 allocation_tid = 3;
- repeated BacktraceFrame allocation_backtrace = 4;
-
- uint64 deallocation_tid = 5;
- repeated BacktraceFrame deallocation_backtrace = 6;
-}
-
-message MemoryError {
- enum Tool {
- GWP_ASAN = 0;
- SCUDO = 1;
-
- reserved 2 to 999;
- }
- Tool tool = 1;
-
- enum Type {
- UNKNOWN = 0;
- USE_AFTER_FREE = 1;
- DOUBLE_FREE = 2;
- INVALID_FREE = 3;
- BUFFER_OVERFLOW = 4;
- BUFFER_UNDERFLOW = 5;
-
- reserved 6 to 999;
- }
- Type type = 2;
-
- oneof location {
- HeapObject heap = 3;
- }
-
- reserved 4 to 999;
-}
-
-message Cause {
- string human_readable = 1;
- oneof details {
- MemoryError memory_error = 2;
- }
-
- reserved 3 to 999;
-}
-
-message Register {
- string name = 1;
- uint64 u64 = 2;
-
- reserved 3 to 999;
-}
-
-message Thread {
- int32 id = 1;
- string name = 2;
- repeated Register registers = 3;
- repeated string backtrace_note = 7;
- repeated BacktraceFrame current_backtrace = 4;
- repeated MemoryDump memory_dump = 5;
- int64 tagged_addr_ctrl = 6;
-
- reserved 8 to 999;
-}
-
-message BacktraceFrame {
- uint64 rel_pc = 1;
- uint64 pc = 2;
- uint64 sp = 3;
-
- string function_name = 4;
- uint64 function_offset = 5;
-
- string file_name = 6;
- uint64 file_map_offset = 7;
- string build_id = 8;
-
- reserved 9 to 999;
-}
-
-message ArmMTEMetadata {
- // One memory tag per granule (e.g. every 16 bytes) of regular memory.
- bytes memory_tags = 1;
- reserved 2 to 999;
-}
-
-message MemoryDump {
- string register_name = 1;
- string mapping_name = 2;
- uint64 begin_address = 3;
- bytes memory = 4;
- oneof metadata {
- ArmMTEMetadata arm_mte_metadata = 6;
- }
-
- reserved 5, 7 to 999;
-}
-
-message MemoryMapping {
- uint64 begin_address = 1;
- uint64 end_address = 2;
- uint64 offset = 3;
-
- bool read = 4;
- bool write = 5;
- bool execute = 6;
-
- string mapping_name = 7;
- string build_id = 8;
- uint64 load_bias = 9;
-
- reserved 10 to 999;
-}
-
-message FD {
- int32 fd = 1;
- string path = 2;
- string owner = 3;
- uint64 tag = 4;
-
- reserved 5 to 999;
-}
-
-message LogBuffer {
- string name = 1;
- repeated LogMessage logs = 2;
-
- reserved 3 to 999;
-}
-
-message LogMessage {
- string timestamp = 1;
- uint32 pid = 2;
- uint32 tid = 3;
- uint32 priority = 4;
- string tag = 5;
- string message = 6;
-
- reserved 7 to 999;
-}
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index f33b2f0c9..bf5386412 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -85,25 +85,26 @@ struct __attribute__((__packed__)) CrashInfoHeader {
uint32_t version;
};
-struct __attribute__((__packed__)) CrashInfoDataStatic {
+struct __attribute__((__packed__)) CrashInfoDataV1 {
siginfo_t siginfo;
ucontext_t ucontext;
uintptr_t abort_msg_address;
};
-struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataStatic {
+struct __attribute__((__packed__)) CrashInfoDataV2 : public CrashInfoDataV1 {
uintptr_t fdsan_table_address;
+};
+
+struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 {
uintptr_t gwp_asan_state;
uintptr_t gwp_asan_metadata;
- uintptr_t scudo_stack_depot;
- uintptr_t scudo_region_info;
- uintptr_t scudo_ring_buffer;
};
struct __attribute__((__packed__)) CrashInfo {
CrashInfoHeader header;
union {
- CrashInfoDataStatic s;
- CrashInfoDataDynamic d;
+ CrashInfoDataV1 v1;
+ CrashInfoDataV2 v2;
+ CrashInfoDataV3 v3;
} data;
};
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 4eac0e92a..254330d51 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -19,7 +19,6 @@ lseek: 1
getdents64: 1
faccessat: 1
recvmsg: 1
-recvfrom: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 21887abe0..9b3ef09ef 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -18,13 +18,12 @@ lseek: 1
getdents64: 1
faccessat: 1
recvmsg: 1
-recvfrom: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 90843fcba..2ef31b0b0 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -24,7 +24,6 @@ lseek: 1
getdents64: 1
faccessat: 1
recvmsg: 1
-recvfrom: 1
process_vm_readv: 1
@@ -34,12 +33,7 @@ rt_sigaction: 1
rt_tgsigqueueinfo: 1
#define PR_SET_VMA 0x53564d41
-#if defined(__aarch64__)
-// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
-#else
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
-#endif
#if 0
libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 4eac0e92a..254330d51 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -19,7 +19,6 @@ lseek: 1
getdents64: 1
faccessat: 1
recvmsg: 1
-recvfrom: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 1585cc6ee..9b3ef09ef 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -18,7 +18,6 @@ lseek: 1
getdents64: 1
faccessat: 1
recvmsg: 1
-recvfrom: 1
process_vm_readv: 1
tgkill: 1
rt_sigprocmask: 1
diff --git a/debuggerd/tombstoned/include/tombstoned/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
index bff216c93..6403dbec4 100644
--- a/debuggerd/tombstoned/include/tombstoned/tombstoned.h
+++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
@@ -23,10 +23,6 @@
#include "dump_type.h"
bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
- android::base::unique_fd* text_output_fd,
- android::base::unique_fd* proto_output_fd, DebuggerdDumpType dump_type);
-
-bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
- android::base::unique_fd* text_output_fd, DebuggerdDumpType dump_type);
+ android::base::unique_fd* output_fd, DebuggerdDumpType dump_type);
bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 613e6f57d..7d25c50a8 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -26,8 +26,8 @@
#include <android-base/cmsg.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
#include "protocol.h"
#include "util.h"
@@ -163,7 +163,7 @@ static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
intercept_close_cb, arg);
- struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
+ struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
event_add(intercept->intercept_event, &timeout);
}
@@ -179,7 +179,7 @@ static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd
intercept->intercept_manager = static_cast<InterceptManager*>(arg);
intercept->sockfd.reset(sockfd);
- struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};
+ struct timeval timeout = { 1, 0 };
event_base* base = evconnlistener_get_base(listener);
event* intercept_event =
event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
@@ -192,18 +192,6 @@ InterceptManager::InterceptManager(event_base* base, int intercept_socket) : bas
/* backlog */ -1, intercept_socket);
}
-bool dump_types_compatible(DebuggerdDumpType interceptor, DebuggerdDumpType dump) {
- if (interceptor == dump) {
- return true;
- }
-
- if (interceptor == kDebuggerdTombstone && dump == kDebuggerdTombstoneProto) {
- return true;
- }
-
- return false;
-}
-
bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
android::base::unique_fd* out_fd) {
auto it = this->intercepts.find(pid);
@@ -214,7 +202,7 @@ bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
if (dump_type == kDebuggerdAnyIntercept) {
LOG(INFO) << "found registered intercept of type " << it->second->dump_type
<< " for requested type kDebuggerdAnyIntercept";
- } else if (!dump_types_compatible(it->second->dump_type, dump_type)) {
+ } else if (it->second->dump_type != dump_type) {
LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
<< " for requested type: " << dump_type;
return false;
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 05d805097..d09b8e860 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -48,8 +48,6 @@
using android::base::GetIntProperty;
using android::base::SendFileDescriptors;
using android::base::StringPrintf;
-
-using android::base::borrowed_fd;
using android::base::unique_fd;
static InterceptManager* intercept_manager;
@@ -59,34 +57,14 @@ enum CrashStatus {
kCrashStatusQueued,
};
-struct CrashArtifact {
- unique_fd fd;
- std::optional<std::string> temporary_path;
-
- static CrashArtifact devnull() {
- CrashArtifact result;
- result.fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
- return result;
- }
-};
-
-struct CrashArtifactPaths {
- std::string text;
- std::optional<std::string> proto;
-};
-
-struct CrashOutput {
- CrashArtifact text;
- std::optional<CrashArtifact> proto;
-};
-
// Ownership of Crash is a bit messy.
// It's either owned by an active event that must have a timeout, or owned by
// queued_requests, in the case that multiple crashes come in at the same time.
struct Crash {
~Crash() { event_free(crash_event); }
- CrashOutput output;
+ std::string crash_tombstone_path;
+ unique_fd crash_tombstone_fd;
unique_fd crash_socket_fd;
pid_t crash_pid;
event* crash_event = nullptr;
@@ -97,15 +75,14 @@ struct Crash {
class CrashQueue {
public:
CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
- size_t max_concurrent_dumps, bool supports_proto)
+ size_t max_concurrent_dumps)
: file_name_prefix_(file_name_prefix),
dir_path_(dir_path),
dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
max_artifacts_(max_artifacts),
next_artifact_(0),
max_concurrent_dumps_(max_concurrent_dumps),
- num_concurrent_dumps_(0),
- supports_proto_(supports_proto) {
+ num_concurrent_dumps_(0) {
if (dir_fd_ == -1) {
PLOG(FATAL) << "failed to open directory: " << dir_path;
}
@@ -121,104 +98,59 @@ class CrashQueue {
return (crash->crash_type == kDebuggerdJavaBacktrace) ? for_anrs() : for_tombstones();
}
- static CrashQueue* for_crash(const std::unique_ptr<Crash>& crash) {
- return for_crash(crash.get());
- }
-
static CrashQueue* for_tombstones() {
static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
GetIntProperty("tombstoned.max_tombstone_count", 32),
- 1 /* max_concurrent_dumps */, true /* supports_proto */);
+ 1 /* max_concurrent_dumps */);
return &queue;
}
static CrashQueue* for_anrs() {
static CrashQueue queue("/data/anr", "trace_" /* file_name_prefix */,
GetIntProperty("tombstoned.max_anr_count", 64),
- 4 /* max_concurrent_dumps */, false /* supports_proto */);
+ 4 /* max_concurrent_dumps */);
return &queue;
}
- CrashArtifact create_temporary_file() const {
- CrashArtifact result;
-
- std::optional<std::string> path;
- result.fd.reset(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0660));
- if (result.fd == -1) {
+ std::pair<std::string, unique_fd> get_output() {
+ std::string path;
+ unique_fd result(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0640));
+ if (result == -1) {
// We might not have O_TMPFILE. Try creating with an arbitrary filename instead.
static size_t counter = 0;
std::string tmp_filename = StringPrintf(".temporary%zu", counter++);
- result.fd.reset(openat(dir_fd_, tmp_filename.c_str(),
- O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0660));
- if (result.fd == -1) {
+ result.reset(openat(dir_fd_, tmp_filename.c_str(),
+ O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0640));
+ if (result == -1) {
PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
}
- result.temporary_path = std::move(tmp_filename);
+ path = StringPrintf("%s/%s", dir_path_.c_str(), tmp_filename.c_str());
}
-
- return std::move(result);
+ return std::make_pair(std::move(path), std::move(result));
}
- std::optional<CrashOutput> get_output(DebuggerdDumpType dump_type) {
- CrashOutput result;
-
- switch (dump_type) {
- case kDebuggerdNativeBacktrace:
- // Don't generate tombstones for native backtrace requests.
- return {};
-
- case kDebuggerdTombstoneProto:
- if (!supports_proto_) {
- LOG(ERROR) << "received kDebuggerdTombstoneProto on a queue that doesn't support proto";
- return {};
- }
- result.proto = create_temporary_file();
- result.text = create_temporary_file();
- break;
-
- case kDebuggerdJavaBacktrace:
- case kDebuggerdTombstone:
- result.text = create_temporary_file();
- break;
-
- default:
- LOG(ERROR) << "unexpected dump type: " << dump_type;
- return {};
- }
-
- return result;
- }
-
- borrowed_fd dir_fd() { return dir_fd_; }
-
- CrashArtifactPaths get_next_artifact_paths() {
- CrashArtifactPaths result;
- result.text = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
-
- if (supports_proto_) {
- result.proto = StringPrintf("%s%02d.pb", file_name_prefix_.c_str(), next_artifact_);
- }
-
+ std::string get_next_artifact_path() {
+ std::string file_name =
+ StringPrintf("%s/%s%02d", dir_path_.c_str(), file_name_prefix_.c_str(), next_artifact_);
next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
- return result;
+ return file_name;
}
- // Consumes crash if it returns true, otherwise leaves it untouched.
- bool maybe_enqueue_crash(std::unique_ptr<Crash>&& crash) {
+ bool maybe_enqueue_crash(Crash* crash) {
if (num_concurrent_dumps_ == max_concurrent_dumps_) {
- queued_requests_.emplace_back(std::move(crash));
+ queued_requests_.push_back(crash);
return true;
}
return false;
}
- void maybe_dequeue_crashes(void (*handler)(std::unique_ptr<Crash> crash)) {
+ void maybe_dequeue_crashes(void (*handler)(Crash* crash)) {
while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {
- std::unique_ptr<Crash> next_crash = std::move(queued_requests_.front());
+ Crash* next_crash = queued_requests_.front();
queued_requests_.pop_front();
- handler(std::move(next_crash));
+ handler(next_crash);
}
}
@@ -232,8 +164,7 @@ class CrashQueue {
time_t oldest_time = std::numeric_limits<time_t>::max();
for (size_t i = 0; i < max_artifacts_; ++i) {
- std::string path =
- StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
+ std::string path = StringPrintf("%s/%s%02zu", dir_path_.c_str(), file_name_prefix_.c_str(), i);
struct stat st;
if (stat(path.c_str(), &st) != 0) {
if (errno == ENOENT) {
@@ -265,9 +196,7 @@ class CrashQueue {
const size_t max_concurrent_dumps_;
size_t num_concurrent_dumps_;
- bool supports_proto_;
-
- std::deque<std::unique_ptr<Crash>> queued_requests_;
+ std::deque<Crash*> queued_requests_;
DISALLOW_COPY_AND_ASSIGN(CrashQueue);
};
@@ -276,61 +205,52 @@ class CrashQueue {
static constexpr bool kJavaTraceDumpsEnabled = true;
// Forward declare the callbacks so they can be placed in a sensible order.
-static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
- void*);
+static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
-static void perform_request(std::unique_ptr<Crash> crash) {
+static void perform_request(Crash* crash) {
unique_fd output_fd;
bool intercepted =
intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
- if (intercepted) {
- if (crash->crash_type == kDebuggerdTombstoneProto) {
- crash->output.proto = CrashArtifact::devnull();
- }
- } else {
- if (auto o = CrashQueue::for_crash(crash.get())->get_output(crash->crash_type); o) {
- crash->output = std::move(*o);
- output_fd.reset(dup(crash->output.text.fd));
+ if (!intercepted) {
+ if (crash->crash_type == kDebuggerdNativeBacktrace) {
+ // Don't generate tombstones for native backtrace requests.
+ output_fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
} else {
- LOG(ERROR) << "failed to get crash output for type " << crash->crash_type;
- return;
+ std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
+ crash->crash_tombstone_fd.reset(dup(output_fd.get()));
}
}
- TombstonedCrashPacket response = {.packet_type = CrashPacketType::kPerformDump};
-
- ssize_t rc = -1;
- if (crash->output.proto) {
- rc = SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get(),
- crash->output.proto->fd.get());
- } else {
- rc = SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
- }
-
+ TombstonedCrashPacket response = {
+ .packet_type = CrashPacketType::kPerformDump
+ };
+ ssize_t rc =
+ SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
output_fd.reset();
if (rc == -1) {
PLOG(WARNING) << "failed to send response to CrashRequest";
- return;
+ goto fail;
} else if (rc != sizeof(response)) {
PLOG(WARNING) << "crash socket write returned short";
- return;
- }
-
- // TODO: Make this configurable by the interceptor?
- struct timeval timeout = {10 * android::base::HwTimeoutMultiplier(), 0};
+ goto fail;
+ } else {
+ // TODO: Make this configurable by the interceptor?
+ struct timeval timeout = { 10, 0 };
- event_base* base = event_get_base(crash->crash_event);
+ event_base* base = event_get_base(crash->crash_event);
+ event_assign(crash->crash_event, base, crash->crash_socket_fd, EV_TIMEOUT | EV_READ,
+ crash_completed_cb, crash);
+ event_add(crash->crash_event, &timeout);
+ }
- event_assign(crash->crash_event, base, crash->crash_socket_fd, EV_TIMEOUT | EV_READ,
- crash_completed_cb, crash.get());
- event_add(crash->crash_event, &timeout);
CrashQueue::for_crash(crash)->on_crash_started();
+ return;
- // The crash is now owned by the event loop.
- crash.release();
+fail:
+ delete crash;
}
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
@@ -340,7 +260,7 @@ static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, so
// TODO: Make sure that only java crashes come in on the java socket
// and only native crashes on the native socket.
- struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};
+ struct timeval timeout = { 1, 0 };
event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
crash->crash_socket_fd.reset(sockfd);
crash->crash_event = crash_event;
@@ -348,37 +268,39 @@ static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, so
}
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
- std::unique_ptr<Crash> crash(static_cast<Crash*>(arg));
+ ssize_t rc;
+ Crash* crash = static_cast<Crash*>(arg);
+
TombstonedCrashPacket request = {};
if ((ev & EV_TIMEOUT) != 0) {
LOG(WARNING) << "crash request timed out";
- return;
+ goto fail;
} else if ((ev & EV_READ) == 0) {
LOG(WARNING) << "tombstoned received unexpected event from crash socket";
- return;
+ goto fail;
}
- ssize_t rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
+ rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
if (rc == -1) {
PLOG(WARNING) << "failed to read from crash socket";
- return;
+ goto fail;
} else if (rc != sizeof(request)) {
LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
<< sizeof(request) << ")";
- return;
+ goto fail;
}
if (request.packet_type != CrashPacketType::kDumpRequest) {
LOG(WARNING) << "unexpected crash packet type, expected kDumpRequest, received "
<< StringPrintf("%#2hhX", request.packet_type);
- return;
+ goto fail;
}
crash->crash_type = request.packet.dump_request.dump_type;
- if (crash->crash_type < 0 || crash->crash_type > kDebuggerdTombstoneProto) {
+ if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) {
LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type;
- return;
+ goto fail;
}
if (crash->crash_type != kDebuggerdJavaBacktrace) {
@@ -392,119 +314,97 @@ static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
if (ret != 0) {
PLOG(ERROR) << "Failed to getsockopt(..SO_PEERCRED)";
- return;
+ goto fail;
}
crash->crash_pid = cr.pid;
}
- pid_t crash_pid = crash->crash_pid;
- LOG(INFO) << "received crash request for pid " << crash_pid;
+ LOG(INFO) << "received crash request for pid " << crash->crash_pid;
- if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(std::move(crash))) {
- LOG(INFO) << "enqueueing crash request for pid " << crash_pid;
+ if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(crash)) {
+ LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
} else {
- perform_request(std::move(crash));
+ perform_request(crash);
}
-}
-static bool rename_tombstone_fd(borrowed_fd fd, borrowed_fd dirfd, const std::string& path) {
- // Always try to unlink the tombstone file.
- // linkat doesn't let us replace a file, so we need to unlink before linking
- // our results onto disk, and if we fail for some reason, we should delete
- // stale tombstones to avoid confusing inconsistency.
- int rc = unlinkat(dirfd.get(), path.c_str(), 0);
- if (rc != 0 && errno != ENOENT) {
- PLOG(ERROR) << "failed to unlink tombstone at " << path;
- return false;
- }
+ return;
- std::string fd_path = StringPrintf("/proc/self/fd/%d", fd.get());
- rc = linkat(AT_FDCWD, fd_path.c_str(), dirfd.get(), path.c_str(), AT_SYMLINK_FOLLOW);
- if (rc != 0) {
- PLOG(ERROR) << "failed to link tombstone at " << path;
- return false;
- }
- return true;
+fail:
+ delete crash;
}
-static void crash_completed(borrowed_fd sockfd, std::unique_ptr<Crash> crash) {
+static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
+ ssize_t rc;
+ Crash* crash = static_cast<Crash*>(arg);
TombstonedCrashPacket request = {};
- CrashQueue* queue = CrashQueue::for_crash(crash);
- ssize_t rc = TEMP_FAILURE_RETRY(read(sockfd.get(), &request, sizeof(request)));
+ CrashQueue::for_crash(crash)->on_crash_completed();
+
+ if ((ev & EV_READ) == 0) {
+ goto fail;
+ }
+
+ rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));
if (rc == -1) {
PLOG(WARNING) << "failed to read from crash socket";
- return;
+ goto fail;
} else if (rc != sizeof(request)) {
LOG(WARNING) << "crash socket received short read of length " << rc << " (expected "
<< sizeof(request) << ")";
- return;
+ goto fail;
}
if (request.packet_type != CrashPacketType::kCompletedDump) {
LOG(WARNING) << "unexpected crash packet type, expected kCompletedDump, received "
<< uint32_t(request.packet_type);
- return;
- }
-
- if (crash->output.text.fd == -1) {
- LOG(WARNING) << "missing output fd";
- return;
+ goto fail;
}
- CrashArtifactPaths paths = queue->get_next_artifact_paths();
+ if (crash->crash_tombstone_fd != -1) {
+ std::string fd_path = StringPrintf("/proc/self/fd/%d", crash->crash_tombstone_fd.get());
+ std::string tombstone_path = CrashQueue::for_crash(crash)->get_next_artifact_path();
- if (rename_tombstone_fd(crash->output.text.fd, queue->dir_fd(), paths.text)) {
- if (crash->crash_type == kDebuggerdJavaBacktrace) {
- LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << paths.text;
- } else {
- // NOTE: Several tools parse this log message to figure out where the
- // tombstone associated with a given native crash was written. Any changes
- // to this message must be carefully considered.
- LOG(ERROR) << "Tombstone written to: " << paths.text;
+ // linkat doesn't let us replace a file, so we need to unlink first.
+ int rc = unlink(tombstone_path.c_str());
+ if (rc != 0 && errno != ENOENT) {
+ PLOG(ERROR) << "failed to unlink tombstone at " << tombstone_path;
+ goto fail;
}
- }
- if (crash->output.proto && crash->output.proto->fd != -1) {
- if (!paths.proto) {
- LOG(ERROR) << "missing path for proto tombstone";
+ rc = linkat(AT_FDCWD, fd_path.c_str(), AT_FDCWD, tombstone_path.c_str(), AT_SYMLINK_FOLLOW);
+ if (rc != 0) {
+ PLOG(ERROR) << "failed to link tombstone";
} else {
- rename_tombstone_fd(crash->output.proto->fd, queue->dir_fd(), *paths.proto);
+ if (crash->crash_type == kDebuggerdJavaBacktrace) {
+ LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << tombstone_path;
+ } else {
+ // NOTE: Several tools parse this log message to figure out where the
+ // tombstone associated with a given native crash was written. Any changes
+ // to this message must be carefully considered.
+ LOG(ERROR) << "Tombstone written to: " << tombstone_path;
+ }
}
- }
- // If we don't have O_TMPFILE, we need to clean up after ourselves.
- if (crash->output.text.temporary_path) {
- rc = unlinkat(queue->dir_fd().get(), crash->output.text.temporary_path->c_str(), 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to unlink temporary tombstone at " << paths.text;
- }
- }
- if (crash->output.proto && crash->output.proto->temporary_path) {
- rc = unlinkat(queue->dir_fd().get(), crash->output.proto->temporary_path->c_str(), 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to unlink temporary proto tombstone";
+ // If we don't have O_TMPFILE, we need to clean up after ourselves.
+ if (!crash->crash_tombstone_path.empty()) {
+ rc = unlink(crash->crash_tombstone_path.c_str());
+ if (rc != 0) {
+ PLOG(ERROR) << "failed to unlink temporary tombstone at " << crash->crash_tombstone_path;
+ }
}
}
-}
-static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
- std::unique_ptr<Crash> crash(static_cast<Crash*>(arg));
+fail:
CrashQueue* queue = CrashQueue::for_crash(crash);
-
- queue->on_crash_completed();
-
- if ((ev & EV_READ) == EV_READ) {
- crash_completed(sockfd, std::move(crash));
- }
+ delete crash;
// If there's something queued up, let them proceed.
queue->maybe_dequeue_crashes(perform_request);
}
int main(int, char* []) {
- umask(0117);
+ umask(0137);
// Don't try to connect to ourselves if we crash.
struct sigaction action = {};
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index c39f4e4f2..b4a1e71fb 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -6,3 +6,6 @@ service tombstoned /system/bin/tombstoned
socket tombstoned_intercept seqpacket 0666 system system
socket tombstoned_java_trace seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
+
+on post-fs-data
+ start tombstoned
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index abfafb152..2c23c98f3 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -32,13 +32,8 @@
using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text_output_fd,
+bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
DebuggerdDumpType dump_type) {
- return tombstoned_connect(pid, tombstoned_socket, text_output_fd, nullptr, dump_type);
-}
-
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text_output_fd,
- unique_fd* proto_output_fd, DebuggerdDumpType dump_type) {
unique_fd sockfd(
socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
: kTombstonedJavaTraceSocketName),
@@ -59,15 +54,8 @@ bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text
return false;
}
- unique_fd tmp_output_fd, tmp_proto_fd;
- ssize_t rc = -1;
-
- if (dump_type == kDebuggerdTombstoneProto) {
- rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd, &tmp_proto_fd);
- } else {
- rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
- }
-
+ unique_fd tmp_output_fd;
+ ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
if (rc == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"failed to read response to DumpRequest packet: %s", strerror(errno));
@@ -90,10 +78,7 @@ bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text
}
*tombstoned_socket = std::move(sockfd);
- *text_output_fd = std::move(tmp_output_fd);
- if (proto_output_fd) {
- *proto_output_fd = std::move(tmp_proto_fd);
- }
+ *output_fd = std::move(tmp_output_fd);
return true;
}
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index ce0fd30f7..a37b3b93b 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -16,7 +16,7 @@
#include "util.h"
-#include <time.h>
+#include <sys/socket.h>
#include <string>
#include <utility>
@@ -24,33 +24,13 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <cutils/sockets.h>
#include "protocol.h"
-std::vector<std::string> get_command_line(pid_t pid) {
- std::vector<std::string> result;
-
- std::string cmdline;
- android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &cmdline);
-
- auto it = cmdline.cbegin();
- while (it != cmdline.cend()) {
- // string::iterator is a wrapped type, not a raw char*.
- auto terminator = std::find(it, cmdline.cend(), '\0');
- result.emplace_back(it, terminator);
- it = std::find_if(terminator, cmdline.cend(), [](char c) { return c != '\0'; });
- }
- if (result.empty()) {
- result.emplace_back("<unknown>");
- }
-
- return result;
-}
-
std::string get_process_name(pid_t pid) {
std::string result = "<unknown>";
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
- // We only want the name, not the whole command line, so truncate at the first NUL.
- return result.c_str();
+ return result;
}
std::string get_thread_name(pid_t tid) {
@@ -58,19 +38,3 @@ std::string get_thread_name(pid_t tid) {
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result);
return android::base::Trim(result);
}
-
-std::string get_timestamp() {
- timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
-
- tm tm;
- localtime_r(&ts.tv_sec, &tm);
-
- char buf[strlen("1970-01-01 00:00:00.123456789+0830") + 1];
- char* s = buf;
- size_t sz = sizeof(buf), n;
- n = strftime(s, sz, "%F %H:%M", &tm), s += n, sz -= n;
- n = snprintf(s, sz, ":%02d.%09ld", tm.tm_sec, ts.tv_nsec), s += n, sz -= n;
- n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
- return buf;
-}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index ec2862a2a..e96442360 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -17,13 +17,9 @@
#pragma once
#include <string>
-#include <vector>
#include <sys/cdefs.h>
#include <sys/types.h>
-std::vector<std::string> get_command_line(pid_t pid);
std::string get_process_name(pid_t pid);
std::string get_thread_name(pid_t tid);
-
-std::string get_timestamp();
diff --git a/deprecated-adf/OWNERS b/deprecated-adf/OWNERS
new file mode 100644
index 000000000..72b8b5a47
--- /dev/null
+++ b/deprecated-adf/OWNERS
@@ -0,0 +1,2 @@
+ghackmann@google.com
+marissaw@google.com
diff --git a/deprecated-adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
new file mode 100644
index 000000000..70f0a3ba0
--- /dev/null
+++ b/deprecated-adf/libadf/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+ name: "libadf",
+ recovery_available: true,
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ srcs: ["adf.cpp"],
+ cflags: ["-Werror"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/deprecated-adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
new file mode 100644
index 000000000..fd9c20895
--- /dev/null
+++ b/deprecated-adf/libadf/adf.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include <linux/limits.h>
+
+#include <sys/ioctl.h>
+
+#include <adf/adf.h>
+
+#define ADF_BASE_PATH "/dev/"
+
+static ssize_t adf_id_vector_to_array(const std::vector<adf_id_t> &in,
+ adf_id_t **out)
+{
+ auto size = sizeof(in[0]) * in.size();
+ // We can't use new[] since the existing API says the caller should free()
+ // the returned array
+ auto ret = static_cast<adf_id_t *>(malloc(size));
+ if (!ret)
+ return -ENOMEM;
+
+ std::copy(in.begin(), in.end(), ret);
+ *out = ret;
+ return in.size();
+}
+
+static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids_out)
+{
+ struct dirent *dirent;
+ std::unique_ptr<DIR, decltype(&closedir)>
+ dir{opendir(ADF_BASE_PATH), closedir};
+ if (!dir)
+ return -errno;
+
+ std::vector<adf_id_t> ids;
+ errno = 0;
+ while ((dirent = readdir(dir.get()))) {
+ adf_id_t id;
+ int matched = sscanf(dirent->d_name, pattern, &id);
+
+ if (matched < 0)
+ return -errno;
+ else if (matched == 1)
+ ids.push_back(id);
+ }
+ if (errno)
+ return -errno;
+
+ return adf_id_vector_to_array(ids, ids_out);
+}
+
+ssize_t adf_devices(adf_id_t **ids)
+{
+ return adf_find_nodes("adf%u", ids);
+}
+
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
+{
+ char filename[64];
+
+ dev->id = id;
+
+ snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id);
+ dev->fd = open(filename, flags);
+ if (dev->fd < 0)
+ return -errno;
+
+ return 0;
+}
+
+void adf_device_close(struct adf_device *dev)
+{
+ if (dev->fd >= 0)
+ close(dev->fd);
+}
+
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data)
+{
+ int err;
+ int ret = 0;
+
+ memset(data, 0, sizeof(*data));
+
+ err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+ if (err < 0)
+ return -ENOMEM;
+
+ if (data->n_attachments)
+ data->attachments = new adf_attachment_config[data->n_attachments];
+
+ if (data->n_allowed_attachments)
+ data->allowed_attachments =
+ new adf_attachment_config[data->n_allowed_attachments];
+
+ if (data->custom_data_size)
+ data->custom_data = new char[data->custom_data_size];
+
+ err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+ if (err < 0) {
+ ret = -errno;
+ adf_free_device_data(data);
+ }
+ return ret;
+}
+
+void adf_free_device_data(struct adf_device_data *data)
+{
+ delete [] data->attachments;
+ data->attachments = nullptr;
+ delete [] data->allowed_attachments;
+ data->allowed_attachments = nullptr;
+ delete [] static_cast<char *>(data->custom_data);
+ data->custom_data = nullptr;
+}
+
+int adf_device_post(struct adf_device *dev,
+ adf_id_t *interfaces, size_t n_interfaces,
+ struct adf_buffer_config *bufs, size_t n_bufs,
+ void *custom_data, size_t custom_data_size)
+{
+ int err;
+ struct adf_post_config data;
+
+ memset(&data, 0, sizeof(data));
+ data.interfaces = interfaces;
+ data.n_interfaces = n_interfaces;
+ data.bufs = bufs;
+ data.n_bufs = n_bufs;
+ data.custom_data = custom_data;
+ data.custom_data_size = custom_data_size;
+
+ err = ioctl(dev->fd, ADF_POST_CONFIG, &data);
+ if (err < 0)
+ return -errno;
+
+ return (int)data.complete_fence;
+}
+
+int adf_device_post_v2(struct adf_device *dev,
+ adf_id_t *interfaces, __u32 n_interfaces,
+ struct adf_buffer_config *bufs, __u32 n_bufs,
+ void *custom_data, __u64 custom_data_size,
+ enum adf_complete_fence_type complete_fence_type,
+ int *complete_fence)
+{
+ int err;
+ struct adf_post_config_v2 data;
+
+ memset(&data, 0, sizeof(data));
+ data.interfaces = (uintptr_t)interfaces;
+ data.n_interfaces = n_interfaces;
+ data.bufs = (uintptr_t)bufs;
+ data.n_bufs = n_bufs;
+ data.custom_data = (uintptr_t)custom_data;
+ data.custom_data_size = custom_data_size;
+ data.complete_fence_type = complete_fence_type;
+
+ err = ioctl(dev->fd, ADF_POST_CONFIG_V2, &data);
+ if (err < 0)
+ return -errno;
+
+ if (complete_fence)
+ *complete_fence = data.complete_fence;
+ else if (data.complete_fence >= 0)
+ close(data.complete_fence);
+
+ return 0;
+}
+
+static int adf_device_attachment(struct adf_device *dev,
+ adf_id_t overlay_engine, adf_id_t interface, bool attach)
+{
+ int err;
+ struct adf_attachment_config data;
+
+ memset(&data, 0, sizeof(data));
+ data.overlay_engine = overlay_engine;
+ data.interface = interface;
+
+ err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data);
+ if (err < 0)
+ return -errno;
+
+ return 0;
+}
+
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface)
+{
+ return adf_device_attachment(dev, overlay_engine, interface, true);
+}
+
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface)
+{
+ return adf_device_attachment(dev, overlay_engine, interface, false);
+}
+
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces)
+{
+ char pattern[64];
+
+ snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id);
+ return adf_find_nodes(pattern, interfaces);
+}
+
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+ adf_id_t overlay_engine, adf_id_t **interfaces)
+{
+ struct adf_device_data data;
+ auto err = adf_get_device_data(dev, &data);
+ if (err < 0)
+ return err;
+
+ std::vector<adf_id_t> ids;
+ if (data.allowed_attachments != nullptr)
+ for (size_t i = 0; i < data.n_allowed_attachments; i++)
+ if (data.allowed_attachments[i].overlay_engine == overlay_engine)
+ ids.push_back(data.allowed_attachments[i].interface);
+
+ adf_free_device_data(&data);
+ return adf_id_vector_to_array(ids, interfaces);
+}
+
+static ssize_t adf_interfaces_filter(struct adf_device *dev,
+ adf_id_t *in, size_t n_in, adf_id_t **out,
+ bool (*filter)(struct adf_interface_data *data, __u32 match),
+ __u32 match)
+{
+ std::vector<adf_id_t> ids;
+ for (size_t i = 0; i < n_in; i++) {
+ int fd = adf_interface_open(dev, in[i], O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ struct adf_interface_data data;
+ auto ret = adf_get_interface_data(fd, &data);
+ close(fd);
+ if (ret < 0)
+ return ret;
+
+ if (filter(&data, match))
+ ids.push_back(in[i]);
+ }
+
+ return adf_id_vector_to_array(ids, out);
+}
+
+static bool adf_interface_type_filter(struct adf_interface_data *data,
+ __u32 type)
+{
+ return data->type == (enum adf_interface_type)type;
+}
+
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+ enum adf_interface_type type,
+ adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+ return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter,
+ type);
+}
+
+static bool adf_interface_flags_filter(struct adf_interface_data *data,
+ __u32 flag)
+{
+ return !!(data->flags & flag);
+}
+
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+ adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+ return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter,
+ flag);
+}
+
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+ char filename[64];
+
+ snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u",
+ dev->id, id);
+
+ int fd = open(filename, flags);
+ if (fd < 0)
+ return -errno;
+ return fd;
+}
+
+int adf_get_interface_data(int fd, struct adf_interface_data *data)
+{
+ int err;
+ int ret = 0;
+
+ memset(data, 0, sizeof(*data));
+
+ err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+ if (err < 0)
+ return -errno;
+
+ if (data->n_available_modes)
+ data->available_modes = new drm_mode_modeinfo[data->n_available_modes];
+
+ if (data->custom_data_size)
+ data->custom_data = new char[data->custom_data_size];
+
+ err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+ if (err < 0) {
+ ret = -errno;
+ adf_free_interface_data(data);
+ }
+ return ret;
+}
+
+void adf_free_interface_data(struct adf_interface_data *data)
+{
+ delete [] data->available_modes;
+ delete [] static_cast<char *>(data->custom_data);
+}
+
+int adf_interface_blank(int fd, __u8 mode)
+{
+ int err = ioctl(fd, ADF_BLANK, mode);
+ if (err < 0)
+ return -errno;
+ return 0;
+}
+
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode)
+{
+ int err = ioctl(fd, ADF_SET_MODE, mode);
+ if (err < 0)
+ return -errno;
+ return 0;
+}
+
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+ __u32 format, __u32 *offset, __u32 *pitch)
+{
+ int err;
+ struct adf_simple_buffer_alloc data;
+
+ memset(&data, 0, sizeof(data));
+ data.w = w;
+ data.h = h;
+ data.format = format;
+
+ err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data);
+ if (err < 0)
+ return -errno;
+
+ *offset = data.offset;
+ *pitch = data.pitch;
+ return (int)data.fd;
+}
+
+static void adf_interface_simple_post_config_buf(struct adf_buffer_config *buf,
+ __u32 overlay_engine, __u32 w, __u32 h, __u32 format, int buf_fd,
+ __u32 offset, __u32 pitch, int acquire_fence)
+{
+ buf->overlay_engine = overlay_engine;
+ buf->w = w;
+ buf->h = h;
+ buf->format = format;
+ buf->fd[0] = buf_fd;
+ buf->offset[0] = offset;
+ buf->pitch[0] = pitch;
+ buf->n_planes = 1;
+ buf->acquire_fence = acquire_fence;
+}
+
+int adf_interface_simple_post(int fd, __u32 overlay_engine,
+ __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+ __u32 pitch, int acquire_fence)
+{
+ int ret;
+ struct adf_simple_post_config data;
+
+ memset(&data, 0, sizeof(data));
+ adf_interface_simple_post_config_buf(&data.buf, overlay_engine, w, h, format,
+ buf_fd, offset, pitch, acquire_fence);
+ ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
+ if (ret < 0)
+ return -errno;
+
+ return (int)data.complete_fence;
+}
+
+int adf_interface_simple_post_v2(int fd, adf_id_t overlay_engine,
+ __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+ __u32 pitch, int acquire_fence,
+ enum adf_complete_fence_type complete_fence_type,
+ int *complete_fence)
+{
+ int ret;
+ struct adf_simple_post_config_v2 data;
+
+ memset(&data, 0, sizeof(data));
+ adf_interface_simple_post_config_buf(&data.buf, overlay_engine, w, h, format,
+ buf_fd, offset, pitch, acquire_fence);
+ data.complete_fence_type = complete_fence_type;
+
+ ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG_V2, &data);
+ if (ret < 0)
+ return -errno;
+
+ if (complete_fence)
+ *complete_fence = data.complete_fence;
+ else if (data.complete_fence >= 0)
+ close(data.complete_fence);
+
+ return 0;
+}
+
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines)
+{
+ char pattern[64];
+
+ snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id);
+ return adf_find_nodes(pattern, overlay_engines);
+}
+
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+ adf_id_t interface, adf_id_t **overlay_engines)
+{
+ struct adf_device_data data;
+ auto err = adf_get_device_data(dev, &data);
+ if (err < 0)
+ return err;
+
+ std::vector<adf_id_t> ids;
+ if (data.allowed_attachments != nullptr)
+ for (size_t i = 0; i < data.n_allowed_attachments; i++)
+ if (data.allowed_attachments[i].interface == interface)
+ ids.push_back(data.allowed_attachments[i].overlay_engine);
+
+ return adf_id_vector_to_array(ids, overlay_engines);
+}
+
+static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
+ adf_id_t *in, size_t n_in, adf_id_t **out,
+ bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
+ void *cookie)
+{
+ std::vector<adf_id_t> ids;
+ size_t i;
+ for (i = 0; i < n_in; i++) {
+ int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ struct adf_overlay_engine_data data;
+ auto ret = adf_get_overlay_engine_data(fd, &data);
+ close(fd);
+ if (ret < 0)
+ return ret;
+
+ if (filter(&data, cookie))
+ ids.push_back(in[i]);
+ }
+
+ return adf_id_vector_to_array(ids, out);
+}
+
+struct format_filter_cookie {
+ const __u32 *formats;
+ size_t n_formats;
+};
+
+static bool adf_overlay_engine_format_filter(
+ struct adf_overlay_engine_data *data, void *cookie)
+{
+ auto c = static_cast<format_filter_cookie *>(cookie);
+ size_t i;
+ for (i = 0; i < data->n_supported_formats; i++) {
+ size_t j;
+ for (j = 0; j < c->n_formats; j++)
+ if (data->supported_formats[i] == c->formats[j])
+ return true;
+ }
+ return false;
+}
+
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+ adf_id_t **out)
+{
+ struct format_filter_cookie cookie = { formats, n_formats };
+ return adf_overlay_engines_filter(dev, in, n_in, out,
+ adf_overlay_engine_format_filter, &cookie);
+}
+
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+ char filename[64];
+
+ snprintf(filename, sizeof(filename),
+ ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id);
+
+ int fd = open(filename, flags);
+ if (fd < 0)
+ return -errno;
+ return fd;
+}
+
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data)
+{
+ int err;
+ int ret = 0;
+
+ memset(data, 0, sizeof(*data));
+
+ err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+ if (err < 0)
+ return -errno;
+
+ if (data->n_supported_formats)
+ data->supported_formats = new __u32[data->n_supported_formats];
+
+ if (data->custom_data_size)
+ data->custom_data = new char[data->custom_data_size];
+
+ err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+ if (err < 0) {
+ ret = -errno;
+ adf_free_overlay_engine_data(data);
+ }
+ return ret;
+}
+
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
+{
+ delete [] data->supported_formats;
+ data->supported_formats = nullptr;
+ delete [] static_cast<char *>(data->custom_data);
+ data->custom_data = nullptr;
+}
+
+bool adf_overlay_engine_supports_format(int fd, __u32 format)
+{
+ struct adf_overlay_engine_data data;
+ bool ret = false;
+ size_t i;
+
+ int err = adf_get_overlay_engine_data(fd, &data);
+ if (err < 0)
+ return false;
+
+ if (data.supported_formats != nullptr) {
+ for (i = 0; i < data.n_supported_formats; i++) {
+ if (data.supported_formats[i] == format) {
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ adf_free_overlay_engine_data(&data);
+ return ret;
+}
+
+int adf_set_event(int fd, enum adf_event_type type, bool enabled)
+{
+ struct adf_set_event data;
+
+ data.type = type;
+ data.enabled = enabled;
+
+ int err = ioctl(fd, ADF_SET_EVENT, &data);
+ if (err < 0)
+ return -errno;
+ return 0;
+}
+
+int adf_read_event(int fd, struct adf_event **event)
+{
+ struct adf_event header;
+ struct event_with_data {
+ struct adf_event base;
+ uint8_t data[0];
+ };
+ using unique_event = std::unique_ptr<event_with_data, decltype(&free)>;
+ size_t data_size;
+
+ int err = read(fd, &header, sizeof(header));
+ if (err < 0)
+ return -errno;
+ if ((size_t)err < sizeof(header))
+ return -EIO;
+ if (header.length < sizeof(header))
+ return -EIO;
+
+ // Again, we can't use new[] since the existing API says the caller should
+ // free() the returned event
+ auto event_ptr = static_cast<event_with_data *>(malloc(header.length));
+ unique_event event_ret{event_ptr, free};
+ if (!event_ret)
+ return -ENOMEM;
+ data_size = header.length - sizeof(header);
+
+ memcpy(event_ret.get(), &header, sizeof(header));
+ ssize_t read_size = read(fd, &event_ret->data, data_size);
+ if (read_size < 0)
+ return -errno;
+ if ((size_t)read_size < data_size)
+ return -EIO;
+
+ *event = &event_ret.release()->base;
+ return 0;
+}
+
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
+{
+ buf[0] = format & 0xFF;
+ buf[1] = (format >> 8) & 0xFF;
+ buf[2] = (format >> 16) & 0xFF;
+ buf[3] = (format >> 24) & 0xFF;
+ buf[4] = '\0';
+}
+
+static bool adf_find_simple_post_overlay_engine(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats,
+ adf_id_t interface, adf_id_t *overlay_engine)
+{
+ adf_id_t *engs = nullptr;
+ ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
+
+ if (engs == nullptr)
+ return false;
+
+ adf_id_t *filtered_engs = nullptr;
+ ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
+ formats, n_formats, engs, n_engs, &filtered_engs);
+ free(engs);
+
+ if (filtered_engs == nullptr)
+ return false;
+
+ *overlay_engine = filtered_engs[0];
+ free(filtered_engs);
+ return true;
+}
+
+static const __u32 any_rgb_format[] = {
+ DRM_FORMAT_C8,
+ DRM_FORMAT_RGB332,
+ DRM_FORMAT_BGR233,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_RGBX5551,
+ DRM_FORMAT_BGRX5551,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_RGBA5551,
+ DRM_FORMAT_BGRA5551,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_RGBX1010102,
+ DRM_FORMAT_BGRX1010102,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_BGRA1010102,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888,
+};
+
+int adf_find_simple_post_configuration(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats,
+ adf_id_t *interface, adf_id_t *overlay_engine)
+{
+ adf_id_t *intfs = NULL;
+ ssize_t n_intfs = adf_interfaces(dev, &intfs);
+
+ if (n_intfs < 0)
+ return n_intfs;
+ else if (!intfs)
+ return -ENODEV;
+
+ adf_id_t *primary_intfs = nullptr;
+ ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
+ ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
+ free(intfs);
+
+ if (n_primary_intfs < 0)
+ return n_primary_intfs;
+ else if (!primary_intfs)
+ return -ENODEV;
+
+ if (!formats) {
+ formats = any_rgb_format;
+ n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]);
+ }
+
+ bool found = false;
+ ssize_t i = 0;
+ for (i = 0; i < n_primary_intfs; i++) {
+ found = adf_find_simple_post_overlay_engine(dev, formats, n_formats,
+ primary_intfs[i], overlay_engine);
+ if (found) {
+ *interface = primary_intfs[i];
+ break;
+ }
+ }
+ free(primary_intfs);
+
+ if (!found)
+ return -ENODEV;
+
+ return 0;
+}
diff --git a/deprecated-adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
new file mode 100644
index 000000000..e4c7b28cc
--- /dev/null
+++ b/deprecated-adf/libadf/include/adf/adf.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBADF_ADF_H_
+#define _LIBADF_ADF_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <video/adf.h>
+
+typedef __u32 adf_id_t;
+
+struct adf_device {
+ adf_id_t id;
+ int fd;
+};
+
+__BEGIN_DECLS
+
+/**
+ * Enumerates all ADF devices.
+ *
+ * Returns the number of ADF devices, and sets ids to a list of device IDs.
+ * The caller must free() the returned list of device IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_devices(adf_id_t **ids);
+
+/**
+ * Opens an ADF device.
+ *
+ * On error, returns -errno.
+ */
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev);
+/**
+ * Closes an ADF device.
+ */
+void adf_device_close(struct adf_device *dev);
+/**
+ * Reads the ADF device data.
+ *
+ * adf_get_device_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_device_data(). On error, returns -errno.
+ */
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data);
+/**
+ * Frees the device data returned by adf_get_device_data().
+ */
+void adf_free_device_data(struct adf_device_data *data);
+
+/**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Returns a sync fence fd that will fire when the configuration is removed
+ * from the screen. On error, returns -errno.
+ */
+int adf_device_post(struct adf_device *dev,
+ adf_id_t *interfaces, size_t n_interfaces,
+ struct adf_buffer_config *bufs, size_t n_bufs,
+ void *custom_data, size_t custom_data_size);
+/**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Compared to adf_device_post(), adf_device_post_v2():
+ *
+ * (*) allows the client to choose the kind of sync fence returned
+ * (through complete_fence_type)
+ *
+ * (*) stores the returned sync fence fd in a provided buffer, so the client
+ * can distinguish between a permission error (ret = -1) and a successful
+ * call that returns no fence (*complete_fence = -1)
+ *
+ * On error, returns -errno.
+ *
+ * On devices without the corresponding kernel support, returns -ENOTTY.
+ */
+int adf_device_post_v2(struct adf_device *dev,
+ adf_id_t *interfaces, __u32 n_interfaces,
+ struct adf_buffer_config *bufs, __u32 n_bufs,
+ void *custom_data, __u64 custom_data_size,
+ enum adf_complete_fence_type complete_fence_type,
+ int *complete_fence);
+
+/**
+ * Attaches the specified interface and overlay engine.
+ */
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface);
+/**
+ * Detaches the specified interface and overlay engine.
+ */
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+ adf_id_t interface);
+
+/**
+ * Enumerates all interfaces belonging to an ADF device.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces);
+
+/**
+ * Enumerates all interfaces which can be attached to the specified overlay
+ * engine.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+ adf_id_t overlay_engine, adf_id_t **interfaces);
+/**
+ * Filters a list of interfaces by type.
+ *
+ * Returns the number of matching interfaces, and sets out to a list of matching
+ * interface IDs. The caller must free() the returned list of interface IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+ enum adf_interface_type type,
+ adf_id_t *in, size_t n_in, adf_id_t **out);
+/**
+ * Filters a list of interfaces by flag.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+ adf_id_t *in, size_t n_in, adf_id_t **out);
+
+/**
+ * Opens an ADF interface.
+ *
+ * Returns a file descriptor. The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the interface data.
+ *
+ * adf_get_interface_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_interface_data(). On error, returns -errno.
+ */
+int adf_get_interface_data(int fd, struct adf_interface_data *data);
+/**
+ * Frees the interface data returned by adf_get_interface_data().
+ */
+void adf_free_interface_data(struct adf_interface_data *data);
+
+/**
+ * Sets the interface's DPMS mode.
+ */
+int adf_interface_blank(int fd, __u8 mode);
+/**
+ * Sets the interface's display mode.
+ */
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode);
+/**
+ * Allocates a single-plane RGB buffer of the specified size and format.
+ *
+ * Returns a dma-buf fd. On error, returns -errno.
+ */
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+ __u32 format, __u32 *offset, __u32 *pitch);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Returns a sync fence fd that will fire when the buffer is removed
+ * from the screen. On error, returns -errno.
+ */
+int adf_interface_simple_post(int fd, adf_id_t overlay_engine,
+ __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+ __u32 pitch, int acquire_fence);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Compared to adf_interface_simple_post(), adf_interface_simple_post_v2():
+ *
+ * (*) allows the client to choose the kind of sync fence returned
+ * (through complete_fence_type)
+ *
+ * (*) stores the returned sync fence fd in a provided buffer, so the client
+ * can distinguish between a permission error (ret = -1) and a successful
+ * call that returns no fence (*complete_fence = -1)
+ *
+ * On error, returns -errno.
+ *
+ * On devices without the corresponding kernel support, returns -ENOTTY.
+ */
+int adf_interface_simple_post_v2(int fd, adf_id_t overlay_engine,
+ __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+ __u32 pitch, int acquire_fence,
+ enum adf_complete_fence_type complete_fence_type,
+ int *complete_fence);
+
+/**
+ * Enumerates all overlay engines belonging to an ADF device.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines);
+
+/**
+ * Enumerates all overlay engines which can be attached to the specified
+ * interface.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+ adf_id_t interface, adf_id_t **overlay_engines);
+/**
+ * Filters a list of overlay engines by supported buffer format.
+ *
+ * Returns the overlay engines which support at least one of the specified
+ * formats. The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+ adf_id_t **out);
+
+/**
+ * Opens an ADF overlay engine.
+ *
+ * Returns a file descriptor. The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the overlay engine data.
+ *
+ * adf_get_overlay_engine_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_overlay_engine_data(). On error, returns
+ * -errno.
+ */
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data);
+/**
+ * Frees the overlay engine data returned by adf_get_overlay_engine_data().
+ */
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data);
+
+/**
+ * Returns whether the overlay engine supports the specified format.
+ */
+bool adf_overlay_engine_supports_format(int fd, __u32 format);
+
+/**
+ * Subscribes or unsubscribes from the specified hardware event.
+ */
+int adf_set_event(int fd, enum adf_event_type type, bool enabled);
+/**
+ * Reads one event from the fd, blocking if needed.
+ *
+ * The caller must free() the returned buffer. On error, returns -errno.
+ */
+int adf_read_event(int fd, struct adf_event **event);
+
+#define ADF_FORMAT_STR_SIZE 5
+/**
+ * Converts an ADF/DRM fourcc format to its string representation.
+ */
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]);
+
+/**
+ * Finds an appropriate interface and overlay engine for a simple post.
+ *
+ * Specifically, finds the primary interface, and an overlay engine
+ * that can be attached to the primary interface and supports one of the
+ * specified formats. The caller may pass a NULL formats list, to indicate that
+ * any RGB format is acceptable.
+ *
+ * On error, returns -errno.
+ */
+int adf_find_simple_post_configuration(struct adf_device *dev,
+ const __u32 *formats, size_t n_formats,
+ adf_id_t *interface, adf_id_t *overlay_engine);
+
+__END_DECLS
+
+#endif /* _LIBADF_ADF_H_ */
diff --git a/deprecated-adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
new file mode 100644
index 000000000..692a4251d
--- /dev/null
+++ b/deprecated-adf/libadf/include/video/adf.h
@@ -0,0 +1,209 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+enum adf_interface_type {
+ ADF_INTF_DSI = 0,
+ ADF_INTF_eDP = 1,
+ ADF_INTF_DPI = 2,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_INTF_VGA = 3,
+ ADF_INTF_DVI = 4,
+ ADF_INTF_HDMI = 5,
+ ADF_INTF_MEMORY = 6,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+ ADF_INTF_TYPE_MAX = (~(__u32) 0),
+};
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+enum adf_event_type {
+ ADF_EVENT_VSYNC = 0,
+ ADF_EVENT_HOTPLUG = 1,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_EVENT_DEVICE_CUSTOM = 128,
+ ADF_EVENT_TYPE_MAX = 255,
+};
+enum adf_complete_fence_type {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ADF_COMPLETE_FENCE_NONE = 0,
+ ADF_COMPLETE_FENCE_PRESENT = 1,
+ ADF_COMPLETE_FENCE_RELEASE = 2,
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_set_event {
+ __u8 type;
+ __u8 enabled;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_event {
+ __u8 type;
+ __u32 length;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_vsync_event {
+ struct adf_event base;
+ __aligned_u64 timestamp;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_hotplug_event {
+ struct adf_event base;
+ __u8 connected;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_PLANES 4
+struct adf_buffer_config {
+ __u32 overlay_engine;
+ __u32 w;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 h;
+ __u32 format;
+ __s32 fd[ADF_MAX_PLANES];
+ __u32 offset[ADF_MAX_PLANES];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 pitch[ADF_MAX_PLANES];
+ __u8 n_planes;
+ __s32 acquire_fence;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+struct adf_post_config {
+ size_t n_interfaces;
+ __u32 __user * interfaces;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t n_bufs;
+ struct adf_buffer_config __user * bufs;
+ size_t custom_data_size;
+ void __user * custom_data;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __s32 complete_fence;
+};
+struct adf_post_config_v2 {
+ __u32 n_interfaces;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u64 interfaces;
+ __u32 n_bufs;
+ __u64 bufs;
+ __u64 custom_data_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u64 custom_data;
+ __s32 complete_fence;
+ __u8 complete_fence_type;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+struct adf_simple_buffer_alloc {
+ __u16 w;
+ __u16 h;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 format;
+ __s32 fd;
+ __u32 offset;
+ __u32 pitch;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct adf_simple_post_config {
+ struct adf_buffer_config buf;
+ __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct adf_simple_post_config_v2 {
+ struct adf_buffer_config buf;
+ __s32 complete_fence;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u8 complete_fence_type;
+};
+struct adf_attachment_config {
+ __u32 overlay_engine;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 interface;
+};
+struct adf_device_data {
+ char name[ADF_NAME_LEN];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t n_attachments;
+ struct adf_attachment_config __user * attachments;
+ size_t n_allowed_attachments;
+ struct adf_attachment_config __user * allowed_attachments;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t custom_data_size;
+ void __user * custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_interface_data {
+ char name[ADF_NAME_LEN];
+ __u32 type;
+ __u32 id;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u32 flags;
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ __u16 height_mm;
+ struct drm_mode_modeinfo current_mode;
+ size_t n_available_modes;
+ struct drm_mode_modeinfo __user * available_modes;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t custom_data_size;
+ void __user * custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct adf_overlay_engine_data {
+ char name[ADF_NAME_LEN];
+ size_t n_supported_formats;
+ __u32 __user * supported_formats;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ size_t custom_data_size;
+ void __user * custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_IOCTL_TYPE 'D'
+#define ADF_IOCTL_NR_CUSTOM 128
+#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_GET_OVERLAY_ENGINE_DATA _IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, struct adf_attachment_config)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, struct adf_attachment_config)
+#define ADF_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 11, struct adf_post_config_v2)
+#define ADF_SIMPLE_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 12, struct adf_simple_post_config_v2)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/deprecated-adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
new file mode 100644
index 000000000..8293c1d8b
--- /dev/null
+++ b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_VIDEO_ADF_H_
+#define _UAPI_VIDEO_ADF_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_mode.h>
+
+#define ADF_NAME_LEN 32
+#define ADF_MAX_CUSTOM_DATA_SIZE 4096
+
+enum adf_interface_type {
+ ADF_INTF_DSI = 0,
+ ADF_INTF_eDP = 1,
+ ADF_INTF_DPI = 2,
+ ADF_INTF_VGA = 3,
+ ADF_INTF_DVI = 4,
+ ADF_INTF_HDMI = 5,
+ ADF_INTF_MEMORY = 6,
+ ADF_INTF_TYPE_DEVICE_CUSTOM = 128,
+ ADF_INTF_TYPE_MAX = (~(__u32)0),
+};
+
+#define ADF_INTF_FLAG_PRIMARY (1 << 0)
+#define ADF_INTF_FLAG_EXTERNAL (1 << 1)
+
+enum adf_event_type {
+ ADF_EVENT_VSYNC = 0,
+ ADF_EVENT_HOTPLUG = 1,
+ ADF_EVENT_DEVICE_CUSTOM = 128,
+ ADF_EVENT_TYPE_MAX = 255,
+};
+
+enum adf_complete_fence_type {
+ /* no fence */
+ ADF_COMPLETE_FENCE_NONE = 0,
+ /* fence fires when the configuration appears on the screen */
+ ADF_COMPLETE_FENCE_PRESENT = 1,
+ /* fence fires when the configuration leaves the screen */
+ ADF_COMPLETE_FENCE_RELEASE = 2,
+};
+
+/**
+ * struct adf_set_event - start or stop subscribing to ADF events
+ *
+ * @type: the type of event to (un)subscribe
+ * @enabled: subscribe or unsubscribe
+ *
+ * After subscribing to an event, userspace may poll() the ADF object's fd
+ * to wait for events or read() to consume the event's data.
+ *
+ * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events.
+ * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1
+ * for driver-private events.
+ */
+struct adf_set_event {
+ __u8 type;
+ __u8 enabled;
+};
+
+/**
+ * struct adf_event - common header for ADF event data
+ *
+ * @type: event type
+ * @length: total size of event data, header inclusive
+ */
+struct adf_event {
+ __u8 type;
+ __u32 length;
+};
+
+/**
+ * struct adf_vsync_event - ADF vsync event
+ *
+ * @base: event header (see &struct adf_event)
+ * @timestamp: time of vsync event, in nanoseconds
+ */
+struct adf_vsync_event {
+ struct adf_event base;
+ __aligned_u64 timestamp;
+};
+
+/**
+ * struct adf_vsync_event - ADF display hotplug event
+ *
+ * @base: event header (see &struct adf_event)
+ * @connected: whether a display is now connected to the interface
+ */
+struct adf_hotplug_event {
+ struct adf_event base;
+ __u8 connected;
+};
+
+#define ADF_MAX_PLANES 4
+/**
+ * struct adf_buffer_config - description of buffer displayed by adf_post_config
+ *
+ * @overlay_engine: id of the target overlay engine
+ * @w: width of display region in pixels
+ * @h: height of display region in pixels
+ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats
+ * @fd: dma_buf fd for each plane
+ * @offset: location of first pixel to scan out, in bytes
+ * @pitch: stride (i.e. length of a scanline including padding) in bytes
+ * @n_planes: number of planes in buffer
+ * @acquire_fence: sync_fence fd which will clear when the buffer is
+ * ready for display, or <0 if the buffer is already ready
+ */
+struct adf_buffer_config {
+ __u32 overlay_engine;
+
+ __u32 w;
+ __u32 h;
+ __u32 format;
+
+ __s32 fd[ADF_MAX_PLANES];
+ __u32 offset[ADF_MAX_PLANES];
+ __u32 pitch[ADF_MAX_PLANES];
+ __u8 n_planes;
+
+ __s32 acquire_fence;
+};
+#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
+
+/**
+ * struct adf_post_config - request to flip to a new set of buffers
+ *
+ * This request is equivalent to &struct adf_post_config_v2 with
+ * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence: sync_fence fd which will clear when this
+ * configuration has left the screen (output)
+ */
+struct adf_post_config {
+ size_t n_interfaces;
+ __u32 __user *interfaces;
+
+ size_t n_bufs;
+ struct adf_buffer_config __user *bufs;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+
+ __s32 complete_fence;
+};
+
+/**
+ * struct adf_post_config_v2 - request to flip to a new set of buffers
+ *
+ * @n_interfaces: number of interfaces targeted by the flip (input)
+ * @interfaces: ids of interfaces targeted by the flip (input)
+ * @n_bufs: number of buffers displayed (input)
+ * @bufs: description of buffers displayed (input)
+ * @custom_data_size: size of driver-private data (input)
+ * @custom_data: driver-private data (input)
+ * @complete_fence_type: one of &enum adf_complete_fence_type describing what
+ * fence to return (input)
+ * @complete_fence: sync_fence fd which will fire at the time
+ * requested by @complete_fence_type (output)
+ */
+struct adf_post_config_v2 {
+ __u32 n_interfaces;
+ __u64 interfaces; /* __u32 * packed into __u64 */
+
+ __u32 n_bufs;
+ __u64 bufs; /* struct adf_buffer_config * packed into __u64 */
+
+ __u64 custom_data_size;
+ __u64 custom_data; /* void * packed into __u64 */
+
+ __s32 complete_fence;
+ __u8 complete_fence_type;
+};
+#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
+
+/**
+ * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer
+ *
+ * @w: width of buffer in pixels (input)
+ * @h: height of buffer in pixels (input)
+ * @format: DRM-style fourcc (input)
+ *
+ * @fd: dma_buf fd (output)
+ * @offset: location of first pixel, in bytes (output)
+ * @pitch: length of a scanline including padding, in bytes (output)
+ *
+ * Simple buffers are analogous to DRM's "dumb" buffers. They have a single
+ * plane of linear RGB data which can be allocated and scanned out without
+ * any driver-private ioctls or data.
+ *
+ * @format must be a standard RGB format defined in drm_fourcc.h.
+ *
+ * ADF clients must NOT assume that an interface can scan out a simple buffer
+ * allocated by a different ADF interface, even if the two interfaces belong to
+ * the same ADF device.
+ */
+struct adf_simple_buffer_alloc {
+ __u16 w;
+ __u16 h;
+ __u32 format;
+
+ __s32 fd;
+ __u32 offset;
+ __u32 pitch;
+};
+
+/**
+ * struct adf_simple_post_config - request to flip to a single buffer without
+ * driver-private data
+ *
+ * This request is equivalent to &struct adf_simple_post_config_v2 with
+ * @complete_fence_type = %ADF_COMPLETE_FENCE_RELEASE.
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence: sync_fence fd which will clear when this buffer has left the
+ * screen (output)
+ */
+struct adf_simple_post_config {
+ struct adf_buffer_config buf;
+ __s32 complete_fence;
+};
+
+/**
+ * struct adf_simple_post_config_v2 - request to flip to a single buffer without
+ * driver-private data
+ *
+ * @buf: description of buffer displayed (input)
+ * @complete_fence_type: one of &enum adf_complete_fence_type describing what
+ * fence to return (input)
+ * @complete_fence: sync_fence fd which will fire at the time
+ * requested by @complete_fence_type (output)
+ */
+struct adf_simple_post_config_v2 {
+ struct adf_buffer_config buf;
+ __s32 complete_fence;
+ __u8 complete_fence_type;
+};
+
+/**
+ * struct adf_attachment_config - description of attachment between an overlay
+ * engine and an interface
+ *
+ * @overlay_engine: id of the overlay engine
+ * @interface: id of the interface
+ */
+struct adf_attachment_config {
+ __u32 overlay_engine;
+ __u32 interface;
+};
+
+/**
+ * struct adf_device_data - describes a display device
+ *
+ * @name: display device's name
+ * @n_attachments: the number of current attachments
+ * @attachments: list of current attachments
+ * @n_allowed_attachments: the number of allowed attachments
+ * @allowed_attachments: list of allowed attachments
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_device_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_attachments;
+ struct adf_attachment_config __user *attachments;
+
+ size_t n_allowed_attachments;
+ struct adf_attachment_config __user *allowed_attachments;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config))
+
+/**
+ * struct adf_device_data - describes a display interface
+ *
+ * @name: display interface's name
+ * @type: interface type (see enum @adf_interface_type)
+ * @id: which interface of type @type;
+ * e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1
+ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
+ * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h)
+ * @hotplug_detect: whether a display is plugged in
+ * @width_mm: screen width in millimeters, or 0 if unknown
+ * @height_mm: screen height in millimeters, or 0 if unknown
+ * @current_mode: current display mode
+ * @n_available_modes: the number of hardware display modes
+ * @available_modes: list of hardware display modes
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_interface_data {
+ char name[ADF_NAME_LEN];
+
+ __u32 type;
+ __u32 id;
+ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */
+ __u32 flags;
+
+ __u8 dpms_state;
+ __u8 hotplug_detect;
+ __u16 width_mm;
+ __u16 height_mm;
+
+ struct drm_mode_modeinfo current_mode;
+ size_t n_available_modes;
+ struct drm_mode_modeinfo __user *available_modes;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo))
+
+/**
+ * struct adf_overlay_engine_data - describes an overlay engine
+ *
+ * @name: overlay engine's name
+ * @n_supported_formats: number of supported formats
+ * @supported_formats: list of supported formats
+ * @custom_data_size: size of driver-private data
+ * @custom_data: driver-private data
+ */
+struct adf_overlay_engine_data {
+ char name[ADF_NAME_LEN];
+
+ size_t n_supported_formats;
+ __u32 __user *supported_formats;
+
+ size_t custom_data_size;
+ void __user *custom_data;
+};
+#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
+
+#define ADF_IOCTL_TYPE 'D'
+#define ADF_IOCTL_NR_CUSTOM 128
+
+#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
+#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
+#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
+#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, \
+ struct drm_mode_modeinfo)
+#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
+#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, \
+ struct adf_interface_data)
+#define ADF_GET_OVERLAY_ENGINE_DATA \
+ _IOR(ADF_IOCTL_TYPE, 6, \
+ struct adf_overlay_engine_data)
+#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, \
+ struct adf_simple_post_config)
+#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, \
+ struct adf_simple_buffer_alloc)
+#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, \
+ struct adf_attachment_config)
+#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, \
+ struct adf_attachment_config)
+
+#define ADF_POST_CONFIG_V2 _IOW(ADF_IOCTL_TYPE, 11, \
+ struct adf_post_config_v2)
+#define ADF_SIMPLE_POST_CONFIG_V2 \
+ _IOW(ADF_IOCTL_TYPE, 12, \
+ struct adf_simple_post_config_v2)
+
+#endif /* _UAPI_VIDEO_ADF_H_ */
diff --git a/deprecated-adf/libadf/tests/Android.bp b/deprecated-adf/libadf/tests/Android.bp
new file mode 100644
index 000000000..9b3430e5f
--- /dev/null
+++ b/deprecated-adf/libadf/tests/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "adf-unit-tests",
+ srcs: ["adf_test.cpp"],
+ shared_libs: ["libsync"],
+ static_libs: ["libadf"],
+ cflags: ["-Werror"],
+}
diff --git a/deprecated-adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
new file mode 100644
index 000000000..82a91f456
--- /dev/null
+++ b/deprecated-adf/libadf/tests/adf_test.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <adf/adf.h>
+#include <gtest/gtest.h>
+#include <sys/mman.h>
+#include <sync/sync.h>
+
+class AdfTest : public testing::Test {
+public:
+ AdfTest() : intf_id(0), intf(-1), eng_id(0), eng(-1) { }
+
+ virtual void SetUp() {
+ int err = adf_device_open(dev_id, O_RDWR, &dev);
+ ASSERT_GE(err, 0) << "opening ADF device " << dev_id <<
+ " failed: " << strerror(-err);
+
+ err = adf_find_simple_post_configuration(&dev, fmt8888, n_fmt8888,
+ &intf_id, &eng_id);
+ ASSERT_GE(err, 0) << "finding ADF configuration failed: " <<
+ strerror(-err);
+
+ intf = adf_interface_open(&dev, intf_id, O_RDWR);
+ ASSERT_GE(intf, 0) << "opening ADF interface " << dev_id << "." <<
+ intf_id << " failed: " << strerror(-intf);
+
+ eng = adf_overlay_engine_open(&dev, eng_id, O_RDWR);
+ ASSERT_GE(eng, 0) << "opening ADF overlay engine " << dev_id << "." <<
+ eng_id << " failed: " << strerror(-eng);
+ }
+
+ virtual void TearDown() {
+ if (eng >= 0)
+ close(eng);
+ if (intf >= 0)
+ close(intf);
+ adf_device_close(&dev);
+ }
+
+ void get8888Format(uint32_t &fmt, char fmt_str[ADF_FORMAT_STR_SIZE]) {
+ adf_overlay_engine_data data;
+ int err = adf_get_overlay_engine_data(eng, &data);
+ ASSERT_GE(err, 0) << "getting ADF overlay engine data failed: " <<
+ strerror(-err);
+
+ for (size_t i = 0; i < data.n_supported_formats; i++) {
+ for (size_t j = 0; j < n_fmt8888; j++) {
+ if (data.supported_formats[i] == fmt8888[j]) {
+ fmt = data.supported_formats[i];
+ adf_format_str(fmt, fmt_str);
+ adf_free_overlay_engine_data(&data);
+ return;
+ }
+ }
+ }
+
+ adf_free_overlay_engine_data(&data);
+ FAIL(); /* this should never happen */
+ }
+
+ /* various helpers to call ADF and die on failure */
+
+ void getInterfaceData(adf_interface_data &data) {
+ int err = adf_get_interface_data(intf, &data);
+ ASSERT_GE(err, 0) << "getting ADF interface data failed: " <<
+ strerror(-err);
+ }
+
+ void getCurrentMode(uint32_t &w, uint32_t &h) {
+ adf_interface_data data;
+ ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
+ w = data.current_mode.hdisplay;
+ h = data.current_mode.vdisplay;
+ adf_free_interface_data(&data);
+ }
+
+ void blank(uint8_t mode) {
+ int err = adf_interface_blank(intf, mode);
+ ASSERT_FALSE(err < 0 && err != -EBUSY) <<
+ "unblanking interface failed: " << strerror(-err);
+ }
+
+ void attach() {
+ int err = adf_device_attach(&dev, eng_id, intf_id);
+ ASSERT_FALSE(err < 0 && err != -EALREADY) <<
+ "attaching overlay engine " << eng_id << " to interface " <<
+ intf_id << " failed: " << strerror(-err);
+ }
+
+ void detach() {
+ int err = adf_device_detach(&dev, eng_id, intf_id);
+ ASSERT_FALSE(err < 0 && err != -EINVAL) <<
+ "detaching overlay engine " << eng_id << " from interface " <<
+ intf_id << " failed: " << strerror(-err);
+ }
+
+ void readVsyncTimestamp(uint64_t &timestamp) {
+ adf_event *event;
+ int err = adf_read_event(intf, &event);
+ ASSERT_GE(err, 0) << "reading ADF event failed: " << strerror(-err);
+
+ ASSERT_EQ(ADF_EVENT_VSYNC, event->type);
+ ASSERT_EQ(sizeof(adf_vsync_event), event->length);
+
+ adf_vsync_event *vsync_event =
+ reinterpret_cast<adf_vsync_event *>(event);
+ timestamp = vsync_event->timestamp;
+ free(event);
+ }
+
+ void drawCheckerboard(uint32_t &w, uint32_t &h, uint32_t &format,
+ char format_str[ADF_FORMAT_STR_SIZE], int &buf_fd, uint32_t &offset,
+ uint32_t &pitch) {
+ ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
+ ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
+
+ buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
+ &pitch);
+ ASSERT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-buf_fd);
+ EXPECT_GE(pitch, w * 4);
+
+ void *mapped = mmap(NULL, pitch * h, PROT_WRITE, MAP_SHARED, buf_fd,
+ offset);
+ ASSERT_NE(mapped, MAP_FAILED) << "mapping " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-errno);
+
+ uint8_t *buf8 = static_cast<uint8_t *>(mapped);
+ for (uint32_t y = 0; y < h / 2; y++) {
+ uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+ for (uint32_t x = 0; x < w / 2; x++)
+ scanline[x] = 0xFF0000FF;
+ for (uint32_t x = w / 2; x < w; x++)
+ scanline[x] = 0xFF00FFFF;
+ }
+ for (uint32_t y = h / 2; y < h; y++) {
+ uint32_t *scanline = reinterpret_cast<uint32_t *>(buf8 + y * pitch);
+ for (uint32_t x = 0; x < w / 2; x++)
+ scanline[x] = 0xFFFF00FF;
+ for (uint32_t x = w / 2; x < w; x++)
+ scanline[x] = 0xFFFFFFFF;
+ }
+
+ munmap(mapped, pitch * h);
+ }
+
+protected:
+ adf_device dev;
+ adf_id_t intf_id;
+ int intf;
+ adf_id_t eng_id;
+ int eng;
+
+private:
+ const static adf_id_t dev_id;
+ const static __u32 fmt8888[];
+ const static size_t n_fmt8888;
+};
+
+const adf_id_t AdfTest::dev_id = 0;
+
+const __u32 AdfTest::fmt8888[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_BGRA8888
+};
+const size_t AdfTest::n_fmt8888 = sizeof(fmt8888) / sizeof(fmt8888[0]);
+
+TEST(adf, devices) {
+ adf_id_t *devs = nullptr;
+ ssize_t n_devs = adf_devices(&devs);
+ free(devs);
+
+ ASSERT_GE(n_devs, 0) << "enumerating ADF devices failed: " <<
+ strerror(-n_devs);
+ ASSERT_TRUE(devs != NULL);
+}
+
+TEST_F(AdfTest, device_data) {
+ adf_device_data data;
+ int err = adf_get_device_data(&dev, &data);
+ ASSERT_GE(err, 0) << "getting ADF device data failed: " << strerror(-err);
+
+ EXPECT_LT(data.n_attachments, ADF_MAX_ATTACHMENTS);
+ EXPECT_GT(data.n_allowed_attachments, 0U);
+ EXPECT_LT(data.n_allowed_attachments, ADF_MAX_ATTACHMENTS);
+ EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
+ adf_free_device_data(&data);
+}
+
+TEST_F(AdfTest, interface_data) {
+ adf_interface_data data;
+ ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
+
+ EXPECT_LT(data.type, ADF_INTF_TYPE_MAX);
+ EXPECT_LE(data.dpms_state, DRM_MODE_DPMS_OFF);
+ EXPECT_EQ(1, data.hotplug_detect);
+ EXPECT_GT(data.n_available_modes, 0U);
+ EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
+ adf_free_interface_data(&data);
+}
+
+TEST_F(AdfTest, overlay_engine_data) {
+ adf_overlay_engine_data data;
+ int err = adf_get_overlay_engine_data(eng, &data);
+ ASSERT_GE(err, 0) << "getting ADF overlay engine failed: " <<
+ strerror(-err);
+
+ EXPECT_GT(data.n_supported_formats, 0U);
+ EXPECT_LT(data.n_supported_formats, ADF_MAX_SUPPORTED_FORMATS);
+ EXPECT_LT(data.custom_data_size, (size_t)ADF_MAX_CUSTOM_DATA_SIZE);
+ adf_free_overlay_engine_data(&data);
+}
+
+TEST_F(AdfTest, blank) {
+ int err = adf_interface_blank(intf, (uint8_t)-1);
+ EXPECT_EQ(-EINVAL, err) << "setting bogus DPMS mode should have failed";
+
+ err = adf_interface_blank(eng, DRM_MODE_DPMS_OFF);
+ EXPECT_EQ(-EINVAL, err) << "blanking overlay engine should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_OFF));
+ err = adf_interface_blank(intf, DRM_MODE_DPMS_OFF);
+ EXPECT_EQ(-EBUSY, err) << "blanking interface twice should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+ err = adf_interface_blank(intf, DRM_MODE_DPMS_ON);
+ EXPECT_EQ(-EBUSY, err) << "unblanking interface twice should have failed";
+
+ adf_interface_data data;
+ ASSERT_NO_FATAL_FAILURE(getInterfaceData(data));
+ EXPECT_EQ(DRM_MODE_DPMS_ON, data.dpms_state);
+ adf_free_interface_data(&data);
+}
+
+TEST_F(AdfTest, event) {
+ int err = adf_set_event(intf, ADF_EVENT_TYPE_MAX, true);
+ EXPECT_EQ(-EINVAL, err) << "enabling bogus ADF event should have failed";
+
+ err = adf_set_event(intf, ADF_EVENT_TYPE_MAX, false);
+ EXPECT_EQ(-EINVAL, err) << "disabling bogus ADF event should have failed";
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, true);
+ ASSERT_GE(err, 0) << "enabling vsync event failed: " << strerror(-err);
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, true);
+ EXPECT_EQ(-EALREADY, err) <<
+ "enabling vsync event twice should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+ uint64_t timestamp1, timestamp2;
+ ASSERT_NO_FATAL_FAILURE(readVsyncTimestamp(timestamp1));
+ ASSERT_NO_FATAL_FAILURE(readVsyncTimestamp(timestamp2));
+ EXPECT_GT(timestamp2, timestamp1);
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, false);
+ EXPECT_GE(err, 0) << "disabling vsync event failed: " << strerror(-err);
+
+ err = adf_set_event(intf, ADF_EVENT_VSYNC, false);
+ EXPECT_EQ(-EALREADY, err) <<
+ "disabling vsync event twice should have failed";
+}
+
+TEST_F(AdfTest, attach) {
+ ASSERT_NO_FATAL_FAILURE(attach());
+ int err = adf_device_attach(&dev, eng_id, intf_id);
+ EXPECT_EQ(-EALREADY, err) << "attaching overlay engine " << eng_id <<
+ " to interface " << intf_id << " twice should have failed";
+
+ ASSERT_NO_FATAL_FAILURE(detach());
+ err = adf_device_detach(&dev, eng_id, intf_id);
+ EXPECT_EQ(-EINVAL, err) << "detaching overlay engine " << eng_id <<
+ " from interface " << intf_id << " twice should have failed";
+
+ err = adf_device_attach(&dev, eng_id, ADF_MAX_INTERFACES);
+ EXPECT_EQ(-EINVAL, err) << "attaching overlay engine " << eng_id <<
+ " to bogus interface should have failed";
+
+ err = adf_device_detach(&dev, eng_id, ADF_MAX_INTERFACES);
+ EXPECT_EQ(-EINVAL, err) << "detaching overlay engine " << eng_id <<
+ " from bogus interface should have failed";
+}
+
+TEST_F(AdfTest, simple_buffer_alloc) {
+ uint32_t w = 0, h = 0;
+ ASSERT_NO_FATAL_FAILURE(getCurrentMode(w, h));
+
+ uint32_t format;
+ char format_str[ADF_FORMAT_STR_SIZE];
+ ASSERT_NO_FATAL_FAILURE(get8888Format(format, format_str));
+
+ uint32_t offset;
+ uint32_t pitch;
+ int buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, format, &offset,
+ &pitch);
+ EXPECT_GE(buf_fd, 0) << "allocating " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-buf_fd);
+ EXPECT_GE(pitch, w * 4);
+ close(buf_fd);
+
+ buf_fd = adf_interface_simple_buffer_alloc(intf, w, h, 0xDEADBEEF, &offset,
+ &pitch);
+ /* n.b.: ADF only allows simple buffers with built-in RGB formats,
+ so this should fail even if a driver supports custom format 0xDEADBEEF */
+ EXPECT_EQ(-EINVAL, buf_fd) <<
+ "allocating buffer with bogus format should have failed";
+}
+
+TEST_F(AdfTest, simple_buffer) {
+ int buf_fd;
+ uint32_t w, h, format, offset, pitch;
+ char format_str[ADF_FORMAT_STR_SIZE];
+ ASSERT_NO_FATAL_FAILURE(drawCheckerboard(w, h, format, format_str,
+ buf_fd, offset, pitch));
+
+ ASSERT_NO_FATAL_FAILURE(attach());
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+ int release_fence = adf_interface_simple_post(intf, eng_id, w, h, format,
+ buf_fd, offset, pitch, -1);
+ close(buf_fd);
+ ASSERT_GE(release_fence, 0) << "posting " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-release_fence);
+ close(release_fence);
+}
+
+TEST_F(AdfTest, simple_buffer_v2) {
+ int buf_fd;
+ uint32_t w, h, format, offset, pitch;
+ char format_str[ADF_FORMAT_STR_SIZE];
+ ASSERT_NO_FATAL_FAILURE(drawCheckerboard(w, h, format, format_str,
+ buf_fd, offset, pitch));
+
+ ASSERT_NO_FATAL_FAILURE(attach());
+ ASSERT_NO_FATAL_FAILURE(blank(DRM_MODE_DPMS_ON));
+
+ int config_1_release;
+ int err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+ format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_RELEASE,
+ &config_1_release);
+ if (err == -ENOTTY) {
+ GTEST_LOG_(INFO) << "ADF_SIMPLE_POST_CONFIG_V2 not supported on this kernel";
+ return;
+ }
+ ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-err);
+
+ err = sync_wait(config_1_release, 1000);
+ ASSERT_EQ(-1, err) <<
+ "waiting for config 1's release fence should not have suceeded";
+ ASSERT_EQ(ETIME, errno) <<
+ "config 1's release fence should have timed out, but failed instead: " <<
+ strerror(errno);
+
+ int config_2_present;
+ err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+ format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_PRESENT,
+ &config_2_present);
+ ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-err);
+
+ err = sync_wait(config_2_present, 1000);
+ ASSERT_EQ(0, err) <<
+ "waiting for config 2's present fence failed: " << strerror(errno);
+ err = sync_wait(config_1_release, 0);
+ ASSERT_EQ(0, err) <<
+ "waiting for config 1's release fence failed: " << strerror(errno);
+ close(config_1_release);
+ close(config_2_present);
+
+ int config_3_no_fence;
+ err = adf_interface_simple_post_v2(intf, eng_id, w, h,
+ format, buf_fd, offset, pitch, -1, ADF_COMPLETE_FENCE_NONE,
+ &config_3_no_fence);
+ ASSERT_GE(err, 0) << "posting " << w << "x" << h << " " <<
+ format_str << " buffer failed: " << strerror(-err);
+ ASSERT_EQ(-1, config_3_no_fence) <<
+ "fence returned even though the fence type was ADF_COMPLETE_FENCE_NONE";
+
+ close(buf_fd);
+}
diff --git a/deprecated-adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
new file mode 100644
index 000000000..57a8d76be
--- /dev/null
+++ b/deprecated-adf/libadfhwc/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libadfhwc",
+ srcs: ["adfhwc.cpp"],
+ static_libs: [
+ "libadf",
+ "liblog",
+ "libutils",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"adfhwc\"",
+ "-Werror",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/deprecated-adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
new file mode 100644
index 000000000..63c0f7521
--- /dev/null
+++ b/deprecated-adf/libadfhwc/adfhwc.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <malloc.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include <log/log.h>
+#include <utils/Vector.h>
+
+#include <adf/adf.h>
+#include <adfhwc/adfhwc.h>
+
+struct adf_hwc_helper {
+ adf_hwc_event_callbacks const *event_cb;
+ void *event_cb_data;
+
+ pthread_t event_thread;
+
+ android::Vector<int> intf_fds;
+ android::Vector<drm_mode_modeinfo> display_configs;
+};
+
+template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
+
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+ int enabled)
+{
+ if (enabled != !!enabled)
+ return -EINVAL;
+
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ switch (event) {
+ case HWC_EVENT_VSYNC:
+ return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled);
+ }
+
+ return -EINVAL;
+}
+
+static inline int32_t dpi(uint16_t res, uint16_t size_mm)
+{
+ if (size_mm)
+ return 1000 * (res * 25.4f) / size_mm;
+ return 0;
+}
+
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON;
+ return adf_interface_blank(dev->intf_fds[disp], dpms_mode);
+}
+
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value)
+{
+ *value = 0;
+ if (dev->intf_fds.size() > 0)
+ *value |= HWC_DISPLAY_PRIMARY_BIT;
+ if (dev->intf_fds.size() > 1)
+ *value |= HWC_DISPLAY_EXTERNAL_BIT;
+
+ return 0;
+}
+
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+ uint32_t *configs, size_t *numConfigs)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ adf_interface_data data;
+ int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+ if (err < 0) {
+ ALOGE("failed to get ADF interface data: %s", strerror(err));
+ return err;
+ }
+
+ if (!data.hotplug_detect)
+ return -ENODEV;
+
+ android::Vector<drm_mode_modeinfo *> unique_configs;
+ unique_configs.push_back(&data.current_mode);
+ for (size_t i = 0; i < data.n_available_modes; i++)
+ if (memcmp(&data.available_modes[i], &data.current_mode,
+ sizeof(data.current_mode)))
+ unique_configs.push_back(&data.available_modes[i]);
+
+ for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) {
+ configs[i] = dev->display_configs.size();
+ dev->display_configs.push_back(*unique_configs[i]);
+ }
+ *numConfigs = unique_configs.size();
+
+ adf_free_interface_data(&data);
+ return 0;
+}
+
+static int32_t adf_display_attribute(const adf_interface_data &data,
+ const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+ switch (attribute) {
+ case HWC_DISPLAY_VSYNC_PERIOD:
+ if (mode.vrefresh)
+ return 1000000000 / mode.vrefresh;
+ return 0;
+
+ case HWC_DISPLAY_WIDTH:
+ return mode.hdisplay;
+
+ case HWC_DISPLAY_HEIGHT:
+ return mode.vdisplay;
+
+ case HWC_DISPLAY_DPI_X:
+ return dpi(mode.hdisplay, data.width_mm);
+
+ case HWC_DISPLAY_DPI_Y:
+ return dpi(mode.vdisplay, data.height_mm);
+
+ default:
+ ALOGE("unknown display attribute %u", attribute);
+ return -EINVAL;
+ }
+}
+
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ if (config >= dev->display_configs.size())
+ return -EINVAL;
+
+ adf_interface_data data;
+ int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+ if (err < 0) {
+ ALOGE("failed to get ADF interface data: %s", strerror(err));
+ return err;
+ }
+
+ for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++)
+ values[i] = adf_display_attribute(data, dev->display_configs[config],
+ attributes[i]);
+
+ adf_free_interface_data(&data);
+ return 0;
+}
+
+static int32_t adf_display_attribute_hwc2(const adf_interface_data &data,
+ const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+ switch (attribute) {
+ case HWC2_ATTRIBUTE_VSYNC_PERIOD:
+ if (mode.vrefresh)
+ return 1000000000 / mode.vrefresh;
+ return 0;
+
+ case HWC2_ATTRIBUTE_WIDTH:
+ return mode.hdisplay;
+
+ case HWC2_ATTRIBUTE_HEIGHT:
+ return mode.vdisplay;
+
+ case HWC2_ATTRIBUTE_DPI_X:
+ return dpi(mode.hdisplay, data.width_mm);
+
+ case HWC2_ATTRIBUTE_DPI_Y:
+ return dpi(mode.vdisplay, data.height_mm);
+
+ default:
+ ALOGE("unknown display attribute %u", attribute);
+ return -EINVAL;
+ }
+}
+
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ if (config >= dev->display_configs.size())
+ return -EINVAL;
+
+ adf_interface_data data;
+ int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+ if (err < 0) {
+ ALOGE("failed to get ADF interface data: %s", strerror(err));
+ return err;
+ }
+
+ for (int i = 0; attributes[i] != HWC2_ATTRIBUTE_INVALID; i++)
+ values[i] = adf_display_attribute_hwc2(data,
+ dev->display_configs[config], attributes[i]);
+
+ adf_free_interface_data(&data);
+ return 0;
+}
+
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ if (config >= dev->display_configs.size())
+ return -EINVAL;
+
+ struct drm_mode_modeinfo mode = dev->display_configs[config];
+
+ return adf_interface_set_mode(dev->intf_fds[disp], &mode);
+}
+
+static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
+{
+ adf_event *event;
+ int err = adf_read_event(dev->intf_fds[disp], &event);
+ if (err < 0) {
+ ALOGE("error reading event from display %d: %s", disp, strerror(err));
+ return;
+ }
+
+ void *vsync_temp;
+ adf_vsync_event *vsync;
+ adf_hotplug_event *hotplug;
+
+ switch (event->type) {
+ case ADF_EVENT_VSYNC:
+ vsync_temp = event;
+ vsync = static_cast<adf_vsync_event *>(vsync_temp);
+ // casting directly to adf_vsync_event * makes g++ warn about
+ // potential alignment issues that don't apply here
+ dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp);
+ break;
+ case ADF_EVENT_HOTPLUG:
+ hotplug = reinterpret_cast<adf_hotplug_event *>(event);
+ dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected);
+ break;
+ default:
+ if (event->type < ADF_EVENT_DEVICE_CUSTOM)
+ ALOGW("unrecognized event type %u", event->type);
+ else if (!dev->event_cb || !dev->event_cb->custom_event)
+ ALOGW("unhandled event type %u", event->type);
+ else
+ dev->event_cb->custom_event(dev->event_cb_data, disp, event);
+ }
+ free(event);
+}
+
+static void *adf_event_thread(void *data)
+{
+ adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data);
+
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+ struct sigaction action = { };
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ action.sa_handler = [](int) { pthread_exit(0); };
+
+ if (sigaction(SIGUSR2, &action, NULL) < 0) {
+ ALOGE("failed to set thread exit action %s", strerror(errno));
+ return NULL;
+ }
+
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGUSR2);
+
+ pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+
+ pollfd fds[dev->intf_fds.size()];
+ for (size_t i = 0; i < dev->intf_fds.size(); i++) {
+ fds[i].fd = dev->intf_fds[i];
+ fds[i].events = POLLIN | POLLPRI;
+ }
+
+ while (true) {
+ if (TEMP_FAILURE_RETRY(poll(fds, dev->intf_fds.size(), -1)) < 0) {
+ ALOGE("error in event thread: %s", strerror(errno));
+ break;
+ }
+
+ for (size_t i = 0; i < dev->intf_fds.size(); i++)
+ if (fds[i].revents & (POLLIN | POLLPRI))
+ handle_adf_event(dev, i);
+ }
+
+ return NULL;
+}
+
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+ const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+ struct adf_hwc_helper **dev)
+{
+ if (!n_intfs)
+ return -EINVAL;
+
+ adf_hwc_helper *dev_ret = new adf_hwc_helper;
+ dev_ret->event_cb = event_cb;
+ dev_ret->event_cb_data = event_cb_data;
+
+ int ret;
+
+ for (size_t i = 0; i < n_intfs; i++) {
+ int dup_intf_fd = dup(intf_fds[i]);
+ if (dup_intf_fd < 0) {
+ ALOGE("failed to dup interface fd: %s", strerror(errno));
+ ret = -errno;
+ goto err;
+ }
+
+ dev_ret->intf_fds.push_back(dup_intf_fd);
+
+ ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1);
+ if (ret < 0 && ret != -EINVAL) {
+ ALOGE("failed to enable hotplug event on display %zu: %s",
+ i, strerror(errno));
+ goto err;
+ }
+ }
+
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGUSR2);
+
+ pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
+
+ ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
+ dev_ret);
+ if (ret) {
+ ALOGE("failed to create event thread: %s", strerror(ret));
+ goto err;
+ }
+
+ *dev = dev_ret;
+ return 0;
+
+err:
+ for (size_t i = 0; i < dev_ret->intf_fds.size(); i++)
+ close(dev_ret->intf_fds[i]);
+
+ delete dev_ret;
+ return ret;
+}
+
+void adf_hwc_close(struct adf_hwc_helper *dev)
+{
+ pthread_kill(dev->event_thread, SIGUSR2);
+ pthread_join(dev->event_thread, NULL);
+
+ for (size_t i = 0; i < dev->intf_fds.size(); i++)
+ close(dev->intf_fds[i]);
+
+ delete dev;
+}
diff --git a/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
new file mode 100644
index 000000000..4f7092507
--- /dev/null
+++ b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBADFHWC_ADFHWC_H_
+#define _LIBADFHWC_ADFHWC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <video/adf.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer2.h>
+
+struct adf_hwc_helper;
+
+struct adf_hwc_event_callbacks {
+ /**
+ * Called on vsync (required)
+ */
+ void (*vsync)(void *data, int disp, uint64_t timestamp);
+ /**
+ * Called on hotplug (required)
+ */
+ void (*hotplug)(void *data, int disp, bool connected);
+ /**
+ * Called on hardware-custom ADF events (optional)
+ */
+ void (*custom_event)(void *data, int disp, struct adf_event *event);
+};
+
+/**
+ * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs.
+ */
+static inline uint32_t adf_fourcc_for_hal_pixel_format(int format)
+{
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return DRM_FORMAT_RGBA8888;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return DRM_FORMAT_RGBX8888;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return DRM_FORMAT_RGB888;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return DRM_FORMAT_RGB565;
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return DRM_FORMAT_BGRA8888;
+ case HAL_PIXEL_FORMAT_YV12:
+ return DRM_FORMAT_YVU420;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ return DRM_FORMAT_NV16;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ return DRM_FORMAT_NV21;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return DRM_FORMAT_YUYV;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Converts HAL display types to equivalent ADF interface flags.
+ */
+static inline uint32_t adf_hwc_interface_flag_for_disp(int disp)
+{
+ switch (disp) {
+ case HWC_DISPLAY_PRIMARY:
+ return ADF_INTF_FLAG_PRIMARY;
+ case HWC_DISPLAY_EXTERNAL:
+ return ADF_INTF_FLAG_EXTERNAL;
+ default:
+ return 0;
+ }
+}
+
+__BEGIN_DECLS
+
+/**
+ * Create a HWC helper for the specified ADF interfaces.
+ *
+ * intf_fds must be indexed by HWC display type: e.g.,
+ * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display
+ * interface. n_intfs must be >= 1.
+ *
+ * The caller retains ownership of the fds in intf_fds and must close()
+ * them when they are no longer needed.
+ *
+ * On error, returns -errno.
+ */
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+ const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+ struct adf_hwc_helper **dev);
+
+/**
+ * Destroys a HWC helper.
+ */
+void adf_hwc_close(struct adf_hwc_helper *dev);
+
+/**
+ * Generic implementations of common HWC ops.
+ *
+ * The HWC should not point its ops directly at these helpers. Instead, the HWC
+ * should provide stub ops which call these helpers after converting the
+ * hwc_composer_device_1* to a struct adf_hwc_helper*.
+ */
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+ int enabled);
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank);
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value);
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+ uint32_t *configs, size_t *numConfigs);
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values);
+/**
+ * Generic implementation of common HWC2 functions.
+ *
+ * The HWC2 should not return these functions directly through getFunction.
+ * Instead, the HWC2 should return stub functions which call these helpers.
+ */
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values);
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config);
+
+__END_DECLS
+
+#endif /* _LIBADFHWC_ADFHWC_H_ */
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index cb79ffe91..93d13bd56 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libdiagnose_usb",
cflags: ["-Wall", "-Wextra", "-Werror"],
diff --git a/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
index 35edb5e05..5695ecec5 100644
--- a/diagnose_usb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -49,7 +49,7 @@ static std::string GetUdevProblem() {
// additionally just to be sure.
if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
// The user is in plugdev so the problem is likely with the udev rules.
- return "missing udev rules? user is in the plugdev group";
+ return "user in plugdev group; are your udev rules wrong?";
}
passwd* pwd = getpwuid(getuid());
return android::base::StringPrintf("user %s is not in the plugdev group",
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 2c70778ae..cf0f1ac93 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -14,34 +14,6 @@
// This is required because no Android.bp can include a library defined in an
// Android.mk. Eventually should kill libfastboot (defined in Android.mk)
-package {
- default_applicable_licenses: ["system_core_fastboot_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "system_core_fastboot_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-BSD",
- ],
- // large-scale-change unable to identify any license_text files
-}
-
cc_library_host_static {
name: "libfastboot2",
@@ -55,7 +27,6 @@ cc_library_host_static {
"tcp.cpp",
"udp.cpp",
"util.cpp",
- "vendor_boot_img_utils.cpp",
"fastboot_driver.cpp",
],
@@ -76,9 +47,7 @@ cc_library_host_static {
],
header_libs: [
- "avb_headers",
"bootimg_headers",
- "libstorage_literals_headers",
],
export_header_lib_headers: [
@@ -127,7 +96,6 @@ cc_defaults {
"-Wextra",
"-Werror",
"-Wvla",
- "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
],
rtti: true,
@@ -142,12 +110,6 @@ cc_binary {
recovery: true,
- product_variables: {
- debuggable: {
- cppflags: ["-DFB_ENABLE_FETCH"],
- },
- },
-
srcs: [
"device/commands.cpp",
"device/fastboot_device.cpp",
@@ -164,7 +126,7 @@ cc_binary {
shared_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
- "android.hardware.fastboot@1.1",
+ "android.hardware.fastboot@1.0",
"android.hardware.health@2.0",
"libasyncio",
"libbase",
@@ -183,19 +145,15 @@ cc_binary {
],
static_libs: [
- "libc++fs",
+ "libgtest_prod",
"libhealthhalutils",
- "libsnapshot_cow",
"libsnapshot_nobinder",
- "update_metadata-protos",
],
header_libs: [
"avb_headers",
- "libgtest_prod_headers",
"libsnapshot_headers",
- "libstorage_literals_headers",
- ],
+ ]
}
cc_defaults {
@@ -208,8 +166,6 @@ cc_defaults {
"-Wextra",
"-Werror",
"-Wunreachable-code",
- "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-D_FILE_OFFSET_BITS=64"
],
target: {
@@ -276,26 +232,15 @@ cc_library_host_static {
"tcp.cpp",
"udp.cpp",
"util.cpp",
- "vendor_boot_img_utils.cpp",
"fastboot_driver.cpp",
],
// Only version the final binaries
use_version_lib: false,
static_libs: ["libbuildversion"],
- header_libs: [
- "avb_headers",
- "libstorage_literals_headers",
- ],
generated_headers: ["platform_tools_version"],
- tidy_flags: [
- // DO NOT add quotes around header-filter flag regex argument,
- // because build/soong will add quotes around the whole flag.
- "-header-filter=(system/core/fastboot/|development/host/windows/usb/api/)",
- ],
-
target: {
windows: {
srcs: ["usb_windows.cpp"],
@@ -305,7 +250,7 @@ cc_library_host_static {
darwin: {
srcs: ["usb_osx.cpp"],
},
- linux: {
+ linux_glibc: {
srcs: ["usb_linux.cpp"],
},
},
@@ -325,7 +270,6 @@ cc_binary_host {
required: [
"mke2fs",
"make_f2fs",
- "make_f2fs_casefold",
],
dist: {
targets: [
@@ -378,33 +322,3 @@ cc_test_host {
},
},
}
-
-cc_test_host {
- name: "fastboot_vendor_boot_img_utils_test",
- srcs: ["vendor_boot_img_utils_test.cpp"],
- static_libs: [
- "libbase",
- "libc++fs",
- "libfastboot",
- "libgmock",
- "liblog",
- ],
- header_libs: [
- "avb_headers",
- "bootimg_headers",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- data: [
- ":fastboot_test_dtb",
- ":fastboot_test_bootconfig",
- ":fastboot_test_vendor_ramdisk_none",
- ":fastboot_test_vendor_ramdisk_platform",
- ":fastboot_test_vendor_ramdisk_replace",
- ":fastboot_test_vendor_boot_v3",
- ":fastboot_test_vendor_boot_v4_without_frag",
- ":fastboot_test_vendor_boot_v4_with_frag"
- ],
-}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 0e918a35a..fd009e768 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -21,7 +21,6 @@ LOCAL_PATH:= $(call my-dir)
my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index a72ee070f..2088ae373 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,4 +1,4 @@
-dvander@google.com
-hridya@google.com
+dpursell@google.com
enh@google.com
jmgao@google.com
+tomcherry@google.com
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index d2056aa41..2c0989ed1 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -34,22 +34,22 @@
#include <stdlib.h>
#include <string.h>
-static void bootimg_set_cmdline_v3_and_above(boot_img_hdr_v3* h, const std::string& cmdline) {
+static void bootimg_set_cmdline_v3(boot_img_hdr_v3* h, const std::string& cmdline) {
if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
}
void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
- if (h->header_version >= 3) {
- return bootimg_set_cmdline_v3_and_above(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);
+ if (h->header_version == 3) {
+ return bootimg_set_cmdline_v3(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);
}
if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
}
-static void mkbootimg_v3_and_above(const std::vector<char>& kernel,
- const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,
- std::vector<char>* out) {
+static boot_img_hdr_v3* mkbootimg_v3(const std::vector<char>& kernel,
+ const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,
+ std::vector<char>* out) {
#define V3_PAGE_SIZE 4096
const size_t page_mask = V3_PAGE_SIZE - 1;
int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
@@ -65,27 +65,22 @@ static void mkbootimg_v3_and_above(const std::vector<char>& kernel,
hdr->ramdisk_size = ramdisk.size();
hdr->os_version = src.os_version;
hdr->header_size = sizeof(boot_img_hdr_v3);
- hdr->header_version = src.header_version;
-
- if (src.header_version >= 4) {
- auto hdr_v4 = reinterpret_cast<boot_img_hdr_v4*>(hdr);
- hdr_v4->signature_size = 0;
- }
+ hdr->header_version = 3;
memcpy(hdr->magic + V3_PAGE_SIZE, kernel.data(), kernel.size());
memcpy(hdr->magic + V3_PAGE_SIZE + kernel_actual, ramdisk.data(), ramdisk.size());
+
+ return hdr;
}
-void mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
- const std::vector<char>& second, const std::vector<char>& dtb, size_t base,
- const boot_img_hdr_v2& src, std::vector<char>* out) {
- if (src.header_version >= 3) {
+boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+ const std::vector<char>& second, const std::vector<char>& dtb,
+ size_t base, const boot_img_hdr_v2& src, std::vector<char>* out) {
+ if (src.header_version == 3) {
if (!second.empty() || !dtb.empty()) {
- die("Second stage bootloader and dtb not supported in v%d boot image\n",
- src.header_version);
+ die("Second stage bootloader and dtb not supported in v3 boot image\n");
}
- mkbootimg_v3_and_above(kernel, ramdisk, src, out);
- return;
+ return reinterpret_cast<boot_img_hdr_v2*>(mkbootimg_v3(kernel, ramdisk, src, out));
}
const size_t page_mask = src.page_size - 1;
@@ -127,4 +122,5 @@ void mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk
second.size());
memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual + second_actual, dtb.data(),
dtb.size());
+ return hdr;
}
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index 0eb003d2f..b7cf9bd90 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -35,8 +35,7 @@
#include <string>
#include <vector>
-void mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
- const std::vector<char>& second, const std::vector<char>& dtb, size_t base,
- const boot_img_hdr_v2& src, std::vector<char>* out);
-
+boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+ const std::vector<char>& second, const std::vector<char>& dtb,
+ size_t base, const boot_img_hdr_v2& src, std::vector<char>* out);
void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 4ea68daff..ba43ca5bd 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -35,7 +35,6 @@
#define FB_CMD_OEM "oem"
#define FB_CMD_GSI "gsi"
#define FB_CMD_SNAPSHOT_UPDATE "snapshot-update"
-#define FB_CMD_FETCH "fetch"
#define RESPONSE_OKAY "OKAY"
#define RESPONSE_FAIL "FAIL"
@@ -78,4 +77,3 @@
#define FB_VAR_FIRST_API_LEVEL "first-api-level"
#define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
#define FB_VAR_TREBLE_ENABLED "treble-enabled"
-#define FB_VAR_MAX_FETCH_SIZE "max-fetch-size"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 0a72812c2..25533535b 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -16,7 +16,6 @@
#include "commands.h"
-#include <inttypes.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -37,7 +36,6 @@
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <libsnapshot/snapshot.h>
-#include <storage_literals/storage_literals.h>
#include <uuid/uuid.h>
#include "constants.h"
@@ -45,12 +43,6 @@
#include "flashing.h"
#include "utility.h"
-#ifdef FB_ENABLE_FETCH
-static constexpr bool kEnableFetch = true;
-#else
-static constexpr bool kEnableFetch = false;
-#endif
-
using android::fs_mgr::MetadataBuilder;
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::BoolResult;
@@ -62,8 +54,6 @@ using ::android::hardware::fastboot::V1_0::Status;
using android::snapshot::SnapshotManager;
using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
-using namespace android::storage_literals;
-
struct VariableHandlers {
// Callback to retrieve the value of a single variable.
std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
@@ -146,9 +136,7 @@ bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args)
{FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
{FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
{FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
- {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
- {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
- };
+ {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}}};
if (args.size() < 2) {
return device->WriteFail("Missing argument");
@@ -176,28 +164,6 @@ bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args)
return device->WriteOkay(message);
}
-bool OemPostWipeData(FastbootDevice* device) {
- auto fastboot_hal = device->fastboot_hal();
- if (!fastboot_hal) {
- return false;
- }
-
- Result ret;
- auto ret_val = fastboot_hal->doOemSpecificErase([&](Result result) { ret = result; });
- if (!ret_val.isOk()) {
- return false;
- }
- if (ret.status == Status::NOT_SUPPORTED) {
- return false;
- } else if (ret.status != Status::SUCCESS) {
- device->WriteStatus(FastbootResult::FAIL, ret.message);
- } else {
- device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
- }
-
- return true;
-}
-
bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
@@ -218,18 +184,7 @@ bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args)
return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
}
if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
- //Perform oem PostWipeData if Android userdata partition has been erased
- bool support_oem_postwipedata = false;
- if (partition_name == "userdata") {
- support_oem_postwipedata = OemPostWipeData(device);
- }
-
- if (!support_oem_postwipedata) {
- return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
- } else {
- //Write device status in OemPostWipeData(), so just return true
- return true;
- }
+ return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
}
return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
}
@@ -240,11 +195,6 @@ bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args)
return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
}
- //Disable "oem postwipedata userdata" to prevent user wipe oem userdata only.
- if (args[0] == "oem postwipedata userdata") {
- return device->WriteStatus(FastbootResult::FAIL, "Unable to do oem postwipedata userdata");
- }
-
Result ret;
auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
if (!ret_val.isOk()) {
@@ -392,13 +342,13 @@ static bool EnterRecovery() {
struct sockaddr_un addr = {.sun_family = AF_UNIX};
strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
- if (connect(sock.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
PLOG(ERROR) << "Couldn't connect to recovery";
return false;
}
// Switch to recovery will not update the boot reason since it does not
// require a reboot.
- auto ret = write(sock.get(), &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
+ auto ret = write(sock, &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
if (ret != sizeof(msg_switch_to_recovery)) {
PLOG(ERROR) << "Couldn't write message to switch to recovery";
return false;
@@ -671,7 +621,7 @@ bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string
return device->WriteFail("No snapshot merge is in progress");
}
- auto sm = SnapshotManager::New();
+ auto sm = SnapshotManager::NewForFirstStageMount();
if (!sm) {
return device->WriteFail("Unable to create SnapshotManager");
}
@@ -683,175 +633,3 @@ bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
-
-namespace {
-// Helper of FetchHandler.
-class PartitionFetcher {
- public:
- static bool Fetch(FastbootDevice* device, const std::vector<std::string>& args) {
- if constexpr (!kEnableFetch) {
- return device->WriteFail("Fetch is not allowed on user build");
- }
-
- if (GetDeviceLockStatus()) {
- return device->WriteFail("Fetch is not allowed on locked devices");
- }
-
- PartitionFetcher fetcher(device, args);
- if (fetcher.Open()) {
- fetcher.Fetch();
- }
- CHECK(fetcher.ret_.has_value());
- return *fetcher.ret_;
- }
-
- private:
- PartitionFetcher(FastbootDevice* device, const std::vector<std::string>& args)
- : device_(device), args_(&args) {}
- // Return whether the partition is successfully opened.
- // If successfully opened, ret_ is left untouched. Otherwise, ret_ is set to the value
- // that FetchHandler should return.
- bool Open() {
- if (args_->size() < 2) {
- ret_ = device_->WriteFail("Missing partition arg");
- return false;
- }
-
- partition_name_ = args_->at(1);
- if (std::find(kAllowedPartitions.begin(), kAllowedPartitions.end(), partition_name_) ==
- kAllowedPartitions.end()) {
- ret_ = device_->WriteFail("Fetch is only allowed on [" +
- android::base::Join(kAllowedPartitions, ", ") + "]");
- return false;
- }
-
- if (!OpenPartition(device_, partition_name_, &handle_, true /* read */)) {
- ret_ = device_->WriteFail(
- android::base::StringPrintf("Cannot open %s", partition_name_.c_str()));
- return false;
- }
-
- partition_size_ = get_block_device_size(handle_.fd());
- if (partition_size_ == 0) {
- ret_ = device_->WriteOkay(android::base::StringPrintf("Partition %s has size 0",
- partition_name_.c_str()));
- return false;
- }
-
- start_offset_ = 0;
- if (args_->size() >= 3) {
- if (!android::base::ParseUint(args_->at(2), &start_offset_)) {
- ret_ = device_->WriteFail("Invalid offset, must be integer");
- return false;
- }
- if (start_offset_ > std::numeric_limits<off64_t>::max()) {
- ret_ = device_->WriteFail(
- android::base::StringPrintf("Offset overflows: %" PRIx64, start_offset_));
- return false;
- }
- }
- if (start_offset_ > partition_size_) {
- ret_ = device_->WriteFail(android::base::StringPrintf(
- "Invalid offset 0x%" PRIx64 ", partition %s has size 0x%" PRIx64, start_offset_,
- partition_name_.c_str(), partition_size_));
- return false;
- }
- uint64_t maximum_total_size_to_read = partition_size_ - start_offset_;
- total_size_to_read_ = maximum_total_size_to_read;
- if (args_->size() >= 4) {
- if (!android::base::ParseUint(args_->at(3), &total_size_to_read_)) {
- ret_ = device_->WriteStatus(FastbootResult::FAIL, "Invalid size, must be integer");
- return false;
- }
- }
- if (total_size_to_read_ == 0) {
- ret_ = device_->WriteOkay("Read 0 bytes");
- return false;
- }
- if (total_size_to_read_ > maximum_total_size_to_read) {
- ret_ = device_->WriteFail(android::base::StringPrintf(
- "Invalid size to read 0x%" PRIx64 ", partition %s has size 0x%" PRIx64
- " and fetching from offset 0x%" PRIx64,
- total_size_to_read_, partition_name_.c_str(), partition_size_, start_offset_));
- return false;
- }
-
- if (total_size_to_read_ > kMaxFetchSizeDefault) {
- ret_ = device_->WriteFail(android::base::StringPrintf(
- "Cannot fetch 0x%" PRIx64
- " bytes because it exceeds maximum transport size 0x%x",
- partition_size_, kMaxDownloadSizeDefault));
- return false;
- }
-
- return true;
- }
-
- // Assume Open() returns true.
- // After execution, ret_ is set to the value that FetchHandler should return.
- void Fetch() {
- CHECK(start_offset_ <= std::numeric_limits<off64_t>::max());
- if (lseek64(handle_.fd(), start_offset_, SEEK_SET) != static_cast<off64_t>(start_offset_)) {
- ret_ = device_->WriteFail(android::base::StringPrintf(
- "On partition %s, unable to lseek(0x%" PRIx64 ": %s", partition_name_.c_str(),
- start_offset_, strerror(errno)));
- return;
- }
-
- if (!device_->WriteStatus(FastbootResult::DATA,
- android::base::StringPrintf(
- "%08x", static_cast<uint32_t>(total_size_to_read_)))) {
- ret_ = false;
- return;
- }
- uint64_t end_offset = start_offset_ + total_size_to_read_;
- std::vector<char> buf(1_MiB);
- uint64_t current_offset = start_offset_;
- while (current_offset < end_offset) {
- // On any error, exit. We can't return a status message to the driver because
- // we are in the middle of writing data, so just let the driver guess what's wrong
- // by ending the data stream prematurely.
- uint64_t remaining = end_offset - current_offset;
- uint64_t chunk_size = std::min<uint64_t>(buf.size(), remaining);
- if (!android::base::ReadFully(handle_.fd(), buf.data(), chunk_size)) {
- PLOG(ERROR) << std::hex << "Unable to read 0x" << chunk_size << " bytes from "
- << partition_name_ << " @ offset 0x" << current_offset;
- ret_ = false;
- return;
- }
- if (!device_->HandleData(false /* is read */, buf.data(), chunk_size)) {
- PLOG(ERROR) << std::hex << "Unable to send 0x" << chunk_size << " bytes of "
- << partition_name_ << " @ offset 0x" << current_offset;
- ret_ = false;
- return;
- }
- current_offset += chunk_size;
- }
-
- ret_ = device_->WriteOkay(android::base::StringPrintf(
- "Fetched %s (offset=0x%" PRIx64 ", size=0x%" PRIx64, partition_name_.c_str(),
- start_offset_, total_size_to_read_));
- }
-
- static constexpr std::array<const char*, 3> kAllowedPartitions{
- "vendor_boot",
- "vendor_boot_a",
- "vendor_boot_b",
- };
-
- FastbootDevice* device_;
- const std::vector<std::string>* args_ = nullptr;
- std::string partition_name_;
- PartitionHandle handle_;
- uint64_t partition_size_ = 0;
- uint64_t start_offset_ = 0;
- uint64_t total_size_to_read_ = 0;
-
- // What FetchHandler should return.
- std::optional<bool> ret_ = std::nullopt;
-};
-} // namespace
-
-bool FetchHandler(FastbootDevice* device, const std::vector<std::string>& args) {
- return PartitionFetcher::Fetch(device, args);
-}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 345ae1afe..c1324bc40 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -20,7 +20,6 @@
#include <vector>
constexpr unsigned int kMaxDownloadSizeDefault = 0x10000000;
-constexpr unsigned int kMaxFetchSizeDefault = 0x10000000;
class FastbootDevice;
@@ -51,4 +50,3 @@ bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>&
bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args);
-bool FetchHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 64a934ddb..1b0859fca 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -22,7 +22,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.0/IBootControl.h>
-#include <android/hardware/fastboot/1.1/IFastboot.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
#include <fs_mgr.h>
#include <fs_mgr/roots.h>
#include <healthhalutils/HealthHalUtils.h>
@@ -37,7 +37,7 @@ using android::fs_mgr::Fstab;
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::IBootControl;
using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::fastboot::V1_1::IFastboot;
+using ::android::hardware::fastboot::V1_0::IFastboot;
using ::android::hardware::health::V2_0::get_health_service;
namespace sph = std::placeholders;
@@ -61,7 +61,6 @@ FastbootDevice::FastbootDevice()
{FB_CMD_OEM, OemCmdHandler},
{FB_CMD_GSI, GsiHandler},
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
- {FB_CMD_FETCH, FetchHandler},
}),
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
@@ -138,19 +137,9 @@ bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& messa
}
bool FastbootDevice::HandleData(bool read, std::vector<char>* data) {
- return HandleData(read, data->data(), data->size());
-}
-
-bool FastbootDevice::HandleData(bool read, char* data, uint64_t size) {
- auto read_write_data_size = read ? this->get_transport()->Read(data, size)
- : this->get_transport()->Write(data, size);
- if (read_write_data_size == -1) {
- LOG(ERROR) << (read ? "read from" : "write to") << " transport failed";
- return false;
- }
- if (static_cast<size_t>(read_write_data_size) != size) {
- LOG(ERROR) << (read ? "read" : "write") << " expected " << size << " bytes, got "
- << read_write_data_size;
+ auto read_write_data_size = read ? this->get_transport()->Read(data->data(), data->size())
+ : this->get_transport()->Write(data->data(), data->size());
+ if (read_write_data_size == -1 || static_cast<size_t>(read_write_data_size) != data->size()) {
return false;
}
return true;
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 35361365c..bbe817244 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -24,7 +24,7 @@
#include <android/hardware/boot/1.0/IBootControl.h>
#include <android/hardware/boot/1.1/IBootControl.h>
-#include <android/hardware/fastboot/1.1/IFastboot.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
#include <android/hardware/health/2.0/IHealth.h>
#include "commands.h"
@@ -40,7 +40,6 @@ class FastbootDevice {
void ExecuteCommands();
bool WriteStatus(FastbootResult result, const std::string& message);
bool HandleData(bool read, std::vector<char>* data);
- bool HandleData(bool read, char* data, uint64_t size);
std::string GetCurrentSlot();
// Shortcuts for writing status results.
@@ -54,7 +53,7 @@ class FastbootDevice {
return boot_control_hal_;
}
android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
- android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal() {
+ android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
return fastboot_hal_;
}
android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
@@ -68,7 +67,7 @@ class FastbootDevice {
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
- android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal_;
+ android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
std::string active_slot_;
};
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index ee0aa582b..a5f12231e 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -27,7 +27,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr_overlayfs.h>
@@ -68,7 +67,7 @@ void WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partit
if ((partition + device->GetCurrentSlot()) == partition_name) {
mount_metadata.emplace();
- android::fs_mgr::TeardownAllOverlayForMountPoint(entry.mount_point);
+ fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
}
}
}
@@ -163,12 +162,8 @@ int Flash(FastbootDevice* device, const std::string& partition_name) {
partition_name == "boot_b")) {
CopyAVBFooter(&data, block_device_size);
}
- if (android::base::GetProperty("ro.system.build.type", "") != "user") {
- WipeOverlayfsForPartition(device, partition_name);
- }
- int result = FlashBlockDevice(handle.fd(), data);
- sync();
- return result;
+ WipeOverlayfsForPartition(device, partition_name);
+ return FlashBlockDevice(handle.fd(), data);
}
bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
@@ -197,8 +192,7 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
- sync();
+ fs_mgr_overlayfs_teardown();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -237,7 +231,6 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip
if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
- sync();
+ fs_mgr_overlayfs_teardown();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4115a6d28..4bee7b20c 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -82,7 +82,7 @@ static int usb_ffs_write(usb_handle* h, const void* data, int len) {
int orig_len = len;
while (len > 0) {
int write_len = std::min(USB_FFS_BULK_SIZE, len);
- int n = write(h->bulk_in.get(), buf, write_len);
+ int n = write(h->bulk_in, buf, write_len);
if (n < 0) {
D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
return -1;
@@ -103,7 +103,7 @@ static int usb_ffs_read(usb_handle* h, void* data, int len, bool allow_partial)
unsigned count = 0;
while (len > 0) {
int read_len = std::min(USB_FFS_BULK_SIZE, len);
- int n = read(h->bulk_out.get(), buf, read_len);
+ int n = read(h->bulk_out, buf, read_len);
if (n < 0) {
D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
return -1;
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 3f9b0f0ac..9c8076528 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -146,7 +146,7 @@ static struct SsFuncDesc ss_descriptors = {
},
};
-#define STR_INTERFACE_ "fastbootd"
+#define STR_INTERFACE_ "fastboot"
static const struct {
struct usb_functionfs_strings_head header;
@@ -248,12 +248,7 @@ ClientUsbTransport::ClientUsbTransport()
}
ssize_t ClientUsbTransport::Read(void* data, size_t len) {
- if (handle_ == nullptr) {
- LOG(ERROR) << "ClientUsbTransport: no handle";
- return -1;
- }
- if (len > SSIZE_MAX) {
- LOG(ERROR) << "ClientUsbTransport: maximum length exceeds bounds";
+ if (handle_ == nullptr || len > SSIZE_MAX) {
return -1;
}
char* char_data = static_cast<char*>(data);
@@ -263,7 +258,6 @@ ssize_t ClientUsbTransport::Read(void* data, size_t len) {
auto bytes_read_now =
handle_->read(handle_.get(), char_data, bytes_to_read, true /* allow_partial */);
if (bytes_read_now < 0) {
- PLOG(ERROR) << "ClientUsbTransport: read failed";
return bytes_read_total == 0 ? -1 : bytes_read_total;
}
bytes_read_total += bytes_read_now;
@@ -283,7 +277,7 @@ ssize_t ClientUsbTransport::Write(const void* data, size_t len) {
size_t bytes_written_total = 0;
while (bytes_written_total < len) {
auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
- auto bytes_written_now = handle_->write(handle_.get(), char_data, bytes_to_write);
+ auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
if (bytes_written_now < 0) {
return bytes_written_total == 0 ? -1 : bytes_written_total;
}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 07ad9028c..7c6ac8993 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -77,8 +77,7 @@ bool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_n
} // namespace
-bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
- bool read) {
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
// We prioritize logical partitions over physical ones, and do this
// consistently for other partition operations (like getvar:partition-size).
if (LogicalPartitionExists(device, name)) {
@@ -90,9 +89,7 @@ bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHan
return false;
}
- int flags = (read ? O_RDONLY : O_WRONLY);
- flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
- unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
+ unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), O_WRONLY | O_EXCL)));
if (fd < 0) {
PLOG(ERROR) << "Failed to open block device: " << handle->path();
return false;
@@ -204,7 +201,12 @@ std::vector<std::string> ListPartitions(FastbootDevice* device) {
}
bool GetDeviceLockStatus() {
- return android::base::GetProperty("ro.boot.verifiedbootstate", "") != "orange";
+ std::string cmdline;
+ // Return lock status true if unable to read kernel command line.
+ if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+ return true;
+ }
+ return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
}
bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index c2646d718..3b71ef006 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -75,11 +75,7 @@ std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partit
std::optional<std::string> FindPhysicalPartition(const std::string& name);
bool LogicalPartitionExists(FastbootDevice* device, const std::string& name,
bool* is_zero_length = nullptr);
-
-// If read, partition is readonly. Else it is write only.
-bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
- bool read = false);
-
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
std::vector<std::string> ListPartitions(FastbootDevice* device);
bool GetDeviceLockStatus();
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index ee1eed876..e7d8bc366 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -33,12 +33,6 @@
#include "flashing.h"
#include "utility.h"
-#ifdef FB_ENABLE_FETCH
-static constexpr bool kEnableFetch = true;
-#else
-static constexpr bool kEnableFetch = false;
-#endif
-
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::Slot;
using ::android::hardware::boot::V1_1::MergeStatus;
@@ -515,13 +509,3 @@ bool GetTrebleEnabled(FastbootDevice* /* device */, const std::vector<std::strin
*message = android::base::GetProperty("ro.treble.enabled", "");
return true;
}
-
-bool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
- std::string* message) {
- if (!kEnableFetch) {
- *message = "fetch not supported on user builds";
- return false;
- }
- *message = android::base::StringPrintf("0x%X", kMaxFetchSizeDefault);
- return true;
-}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index f40a0257e..c11e472b7 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -80,8 +80,6 @@ bool GetSecurityPatchLevel(FastbootDevice* device, const std::vector<std::string
std::string* message);
bool GetTrebleEnabled(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
-bool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
- std::string* message);
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index f5a3384cf..cb1d3541c 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@ _fastboot_cmd_flash() {
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $i -eq $COMP_CWORD ]]; then
- partitions="boot bootloader dtbo modem odm odm_dlkm oem product pvmfw radio recovery system vbmeta vendor vendor_dlkm"
+ partitions="boot bootloader dtbo modem odm oem product radio recovery system vbmeta vendor"
COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
else
_fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6a49fdf34..5307a0081 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -76,15 +76,12 @@
#include "udp.h"
#include "usb.h"
#include "util.h"
-#include "vendor_boot_img_utils.h"
-using android::base::borrowed_fd;
using android::base::ReadFully;
using android::base::Split;
using android::base::Trim;
using android::base::unique_fd;
using namespace std::string_literals;
-using namespace std::placeholders;
static const char* serial = nullptr;
@@ -117,7 +114,7 @@ struct fastboot_buffer {
enum fb_buffer_type type;
void* data;
int64_t sz;
- unique_fd fd;
+ int fd;
int64_t image_size;
};
@@ -148,9 +145,7 @@ static Image images[] = {
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, ImageType::BootCritical },
{ "dts", "dt.img", "dt.sig", "dts", true, ImageType::BootCritical },
{ "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal },
- { "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
- { "pvmfw", "pvmfw.img", "pvmfw.sig", "pvmfw", true, ImageType::BootCritical },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
{ "system", "system.img", "system.sig", "system", false, ImageType::Normal },
@@ -166,20 +161,11 @@ static Image images[] = {
"vbmeta_system.sig",
"vbmeta_system",
true, ImageType::BootCritical },
- { "vbmeta_vendor",
- "vbmeta_vendor.img",
- "vbmeta_vendor.sig",
- "vbmeta_vendor",
- true, ImageType::BootCritical },
{ "vendor", "vendor.img", "vendor.sig", "vendor", true, ImageType::Normal },
{ "vendor_boot",
"vendor_boot.img", "vendor_boot.sig",
"vendor_boot",
true, ImageType::BootCritical },
- { "vendor_dlkm",
- "vendor_dlkm.img", "vendor_dlkm.sig",
- "vendor_dlkm",
- true, ImageType::Normal },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
@@ -214,10 +200,8 @@ static std::string find_item(const std::string& item) {
double last_start_time;
static void Status(const std::string& message) {
- if (!message.empty()) {
- static constexpr char kStatusFormat[] = "%-50s ";
- fprintf(stderr, kStatusFormat, message.c_str());
- }
+ static constexpr char kStatusFormat[] = "%-50s ";
+ fprintf(stderr, kStatusFormat, message.c_str());
last_start_time = now();
}
@@ -235,9 +219,9 @@ static void InfoMessage(const std::string& info) {
fprintf(stderr, "(bootloader) %s\n", info.c_str());
}
-static int64_t get_file_size(borrowed_fd fd) {
+static int64_t get_file_size(int fd) {
struct stat sb;
- if (fstat(fd.get(), &sb) == -1) {
+ if (fstat(fd, &sb) == -1) {
die("could not get file size");
}
return sb.st_size;
@@ -274,10 +258,6 @@ static int match_fastboot(usb_ifc_info* info) {
static int list_devices_callback(usb_ifc_info* info) {
if (match_fastboot_with_serial(info, nullptr) == 0) {
std::string serial = info->serial_number;
- std::string interface = info->interface;
- if (interface.empty()) {
- interface = "fastboot";
- }
if (!info->writable) {
serial = UsbNoPermissionsShortHelpText();
}
@@ -286,9 +266,9 @@ static int list_devices_callback(usb_ifc_info* info) {
}
// output compatible with "adb devices"
if (!g_long_listing) {
- printf("%s\t%s", serial.c_str(), interface.c_str());
+ printf("%s\tfastboot", serial.c_str());
} else {
- printf("%-22s %s", serial.c_str(), interface.c_str());
+ printf("%-22s fastboot", serial.c_str());
if (strlen(info->device_path) > 0) printf(" %s", info->device_path);
}
putchar('\n');
@@ -416,20 +396,12 @@ static int show_help() {
" gsi wipe|disable Wipe or disable a GSI installation (fastbootd only).\n"
" wipe-super [SUPER_EMPTY] Wipe the super partition. This will reset it to\n"
" contain an empty set of default dynamic partitions.\n"
- " create-logical-partition NAME SIZE\n"
- " Create a logical partition with the given name and\n"
- " size, in the super partition.\n"
- " delete-logical-partition NAME\n"
- " Delete a logical partition with the given name.\n"
- " resize-logical-partition NAME SIZE\n"
- " Change the size of the named logical partition.\n"
" snapshot-update cancel On devices that support snapshot-based updates, cancel\n"
" an in-progress update. This may make the device\n"
" unbootable until it is reflashed.\n"
" snapshot-update merge On devices that support snapshot-based updates, finish\n"
" an in-progress update if it is in the \"merging\"\n"
" phase.\n"
- " fetch PARTITION Fetch a partition image from the device."
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
@@ -471,8 +443,6 @@ static int show_help() {
" --skip-reboot Don't reboot device after flashing.\n"
" --disable-verity Sets disable-verity when flashing vbmeta.\n"
" --disable-verification Sets disable-verification when flashing vbmeta.\n"
- " --fs-options=OPTION[,OPTION]\n"
- " Enable filesystem features. OPTION supports casefold, projid, compress\n"
#if !defined(_WIN32)
" --wipe-and-use-fbe Enable file-based encryption, wiping userdata.\n"
#endif
@@ -482,7 +452,7 @@ static int show_help() {
" --version Display version.\n"
" --help, -h Show this message.\n"
);
- // clang-format on
+ // clang-format off
return 0;
}
@@ -535,27 +505,22 @@ static std::vector<char> LoadBootableImage(const std::string& kernel, const std:
fprintf(stderr,"creating boot image...\n");
std::vector<char> out;
- mkbootimg(kernel_data, ramdisk_data, second_stage_data, dtb_data, g_base_addr, g_boot_img_hdr,
- &out);
+ boot_img_hdr_v2* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+ dtb_data, g_base_addr, g_boot_img_hdr, &out);
- if (!g_cmdline.empty()) {
- bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v2*>(out.data()), g_cmdline);
- }
+ if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
return out;
}
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipEntry64 zip_entry;
+ ZipEntry zip_entry;
if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
- if (zip_entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
- die("entry '%s' is too large: %" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);
- }
out->resize(zip_entry.uncompressed_length);
fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
@@ -658,34 +623,34 @@ static void delete_fbemarker_tmpdir(const std::string& dir) {
}
}
-static unique_fd unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
+static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipEntry64 zip_entry;
+ ZipEntry zip_entry;
if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
- return unique_fd();
+ return -1;
}
- fprintf(stderr, "extracting %s (%" PRIu64 " MB) to disk...", entry_name,
+ fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
zip_entry.uncompressed_length / 1024 / 1024);
double start = now();
- int error = ExtractEntryToFile(zip, &zip_entry, fd.get());
+ int error = ExtractEntryToFile(zip, &zip_entry, fd);
if (error != 0) {
die("\nfailed to extract '%s': %s", entry_name, ErrorCodeString(error));
}
- if (lseek(fd.get(), 0, SEEK_SET) != 0) {
+ if (lseek(fd, 0, SEEK_SET) != 0) {
die("\nlseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
}
fprintf(stderr, " took %.3fs\n", now() - start);
- return fd;
+ return fd.release();
}
-static bool CheckRequirement(const std::string& cur_product, const std::string& var,
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
const std::string& product, bool invert,
const std::vector<std::string>& options) {
Status("Checking '" + var + "'");
@@ -697,7 +662,7 @@ static bool CheckRequirement(const std::string& cur_product, const std::string&
double split = now();
fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
cur_product.c_str(), product.c_str(), (split - start));
- return true;
+ return;
}
}
@@ -706,7 +671,7 @@ static bool CheckRequirement(const std::string& cur_product, const std::string&
fprintf(stderr, "FAILED\n\n");
fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
fb->Error().c_str());
- return false;
+ die("requirements not met!");
}
bool match = false;
@@ -726,7 +691,7 @@ static bool CheckRequirement(const std::string& cur_product, const std::string&
if (match) {
double split = now();
fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
- return true;
+ return;
}
fprintf(stderr, "FAILED\n\n");
@@ -736,7 +701,7 @@ static bool CheckRequirement(const std::string& cur_product, const std::string&
fprintf(stderr, " or '%s'", it->c_str());
}
fprintf(stderr, ".\n\n");
- return false;
+ die("requirements not met!");
}
bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
@@ -800,7 +765,7 @@ static void HandlePartitionExists(const std::vector<std::string>& options) {
}
}
-static void CheckRequirements(const std::string& data, bool force_flash) {
+static void CheckRequirements(const std::string& data) {
std::string cur_product;
if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
@@ -824,14 +789,7 @@ static void CheckRequirements(const std::string& data, bool force_flash) {
if (name == "partition-exists") {
HandlePartitionExists(options);
} else {
- bool met = CheckRequirement(cur_product, name, product, invert, options);
- if (!met) {
- if (!force_flash) {
- die("requirements not met!");
- } else {
- fprintf(stderr, "requirements not met! but proceeding due to --force\n");
- }
- }
+ CheckRequirement(cur_product, name, product, invert, options);
}
}
}
@@ -876,23 +834,24 @@ static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
return out_s;
}
-static uint64_t get_uint_var(const char* var_name) {
- std::string value_str;
- if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
- verbose("target didn't report %s", var_name);
+static int64_t get_target_sparse_limit() {
+ std::string max_download_size;
+ if (fb->GetVar("max-download-size", &max_download_size) != fastboot::SUCCESS ||
+ max_download_size.empty()) {
+ verbose("target didn't report max-download-size");
return 0;
}
// Some bootloaders (angler, for example) send spurious whitespace too.
- value_str = android::base::Trim(value_str);
+ max_download_size = android::base::Trim(max_download_size);
- uint64_t value;
- if (!android::base::ParseUint(value_str, &value)) {
- fprintf(stderr, "couldn't parse %s '%s'\n", var_name, value_str.c_str());
+ uint64_t limit;
+ if (!android::base::ParseUint(max_download_size, &limit)) {
+ fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
return 0;
}
- if (value > 0) verbose("target reported %s of %" PRId64 " bytes", var_name, value);
- return value;
+ if (limit > 0) verbose("target reported max download size of %" PRId64 " bytes", limit);
+ return limit;
}
static int64_t get_sparse_limit(int64_t size) {
@@ -901,7 +860,7 @@ static int64_t get_sparse_limit(int64_t size) {
// Unlimited, so see what the target device's limit is.
// TODO: shouldn't we apply this limit even if you've used -S?
if (target_sparse_limit == -1) {
- target_sparse_limit = static_cast<int64_t>(get_uint_var("max-download-size"));
+ target_sparse_limit = get_target_sparse_limit();
}
if (target_sparse_limit > 0) {
limit = target_sparse_limit;
@@ -917,24 +876,23 @@ static int64_t get_sparse_limit(int64_t size) {
return 0;
}
-static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(int fd, struct fastboot_buffer* buf) {
int64_t sz = get_file_size(fd);
if (sz == -1) {
return false;
}
- if (sparse_file* s = sparse_file_import(fd.get(), false, false)) {
+ if (sparse_file* s = sparse_file_import(fd, false, false)) {
buf->image_size = sparse_file_len(s, false, false);
sparse_file_destroy(s);
} else {
buf->image_size = sz;
}
- lseek(fd.get(), 0, SEEK_SET);
+ lseek(fd, 0, SEEK_SET);
int64_t limit = get_sparse_limit(sz);
- buf->fd = std::move(fd);
if (limit) {
- sparse_file** s = load_sparse_files(buf->fd.get(), limit);
+ sparse_file** s = load_sparse_files(fd, limit);
if (s == nullptr) {
return false;
}
@@ -943,6 +901,7 @@ static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf) {
} else {
buf->type = FB_BUFFER_FD;
buf->data = nullptr;
+ buf->fd = fd;
buf->sz = sz;
}
@@ -957,7 +916,7 @@ static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
}
struct stat s;
- if (fstat(fd.get(), &s)) {
+ if (fstat(fd, &s)) {
return false;
}
if (!S_ISREG(s.st_mode)) {
@@ -965,7 +924,7 @@ static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
return false;
}
- return load_buf_fd(std::move(fd), buf);
+ return load_buf_fd(fd.release(), buf);
}
static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
@@ -1011,12 +970,13 @@ static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_bo
data[flags_offset] |= 0x02;
}
- unique_fd fd(make_temporary_fd("vbmeta rewriting"));
+ int fd = make_temporary_fd("vbmeta rewriting");
if (!android::base::WriteStringToFd(data, fd)) {
die("Failed writing to modified vbmeta");
}
- buf->fd = std::move(fd);
- lseek(buf->fd.get(), 0, SEEK_SET);
+ close(buf->fd);
+ buf->fd = fd;
+ lseek(fd, 0, SEEK_SET);
}
static bool has_vbmeta_partition() {
@@ -1026,11 +986,6 @@ static bool has_vbmeta_partition() {
fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
}
-static bool is_logical(const std::string& partition) {
- std::string value;
- return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
-}
-
static std::string fb_fix_numeric_var(std::string var) {
// Some bootloaders (angler, for example), send spurious leading whitespace.
var = android::base::Trim(var);
@@ -1040,30 +995,27 @@ static std::string fb_fix_numeric_var(std::string var) {
return var;
}
-static uint64_t get_partition_size(const std::string& partition) {
+static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+ if (buf->sz < AVB_FOOTER_SIZE) {
+ return;
+ }
+
std::string partition_size_str;
if (fb->GetVar("partition-size:" + partition, &partition_size_str) != fastboot::SUCCESS) {
- if (!is_logical(partition)) {
- return 0;
- }
- die("cannot get partition size for %s", partition.c_str());
+ die("cannot get boot partition size");
}
partition_size_str = fb_fix_numeric_var(partition_size_str);
- uint64_t partition_size;
- if (!android::base::ParseUint(partition_size_str, &partition_size)) {
- if (!is_logical(partition)) {
- return 0;
- }
+ int64_t partition_size;
+ if (!android::base::ParseInt(partition_size_str, &partition_size)) {
die("Couldn't parse partition size '%s'.", partition_size_str.c_str());
}
- return partition_size;
-}
-
-static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
- if (buf->sz < AVB_FOOTER_SIZE) {
+ if (partition_size == buf->sz) {
return;
}
+ if (partition_size < buf->sz) {
+ die("boot partition is smaller than boot image");
+ }
std::string data;
if (!android::base::ReadFdToString(buf->fd, &data)) {
@@ -1074,27 +1026,19 @@ static void copy_boot_avb_footer(const std::string& partition, struct fastboot_b
if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
return;
}
- // If overflows and negative, it should be < buf->sz.
- int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
-
- if (partition_size == buf->sz) {
- return;
- }
- if (partition_size < buf->sz) {
- die("boot partition is smaller than boot image");
- }
- unique_fd fd(make_temporary_fd("boot rewriting"));
+ int fd = make_temporary_fd("boot rewriting");
if (!android::base::WriteStringToFd(data, fd)) {
die("Failed writing to modified boot");
}
- lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);
+ lseek(fd, partition_size - AVB_FOOTER_SIZE, SEEK_SET);
if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
die("Failed copying AVB footer in boot");
}
- buf->fd = std::move(fd);
+ close(buf->fd);
+ buf->fd = fd;
buf->sz = partition_size;
- lseek(buf->fd.get(), 0, SEEK_SET);
+ lseek(fd, 0, SEEK_SET);
}
static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
@@ -1142,7 +1086,6 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
static std::string get_current_slot() {
std::string current_slot;
if (fb->GetVar("current-slot", &current_slot) != fastboot::SUCCESS) return "";
- if (current_slot[0] == '_') current_slot.erase(0, 1);
return current_slot;
}
@@ -1224,10 +1167,8 @@ static void do_for_partition(const std::string& part, const std::string& slot,
const std::function<void(const std::string&)>& func, bool force_slot) {
std::string has_slot;
std::string current_slot;
- // |part| can be vendor_boot:default. Append slot to the first token.
- auto part_tokens = android::base::Split(part, ":");
- if (fb->GetVar("has-slot:" + part_tokens[0], &has_slot) != fastboot::SUCCESS) {
+ if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
/* If has-slot is not supported, the answer is no. */
has_slot = "no";
}
@@ -1237,15 +1178,14 @@ static void do_for_partition(const std::string& part, const std::string& slot,
if (current_slot == "") {
die("Failed to identify current slot");
}
- part_tokens[0] += "_" + current_slot;
+ func(part + "_" + current_slot);
} else {
- part_tokens[0] += "_" + slot;
+ func(part + '_' + slot);
}
- func(android::base::Join(part_tokens, ":"));
} else {
if (force_slot && slot != "") {
- fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n",
- part_tokens[0].c_str(), slot.c_str());
+ fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n",
+ part.c_str(), slot.c_str());
}
func(part);
}
@@ -1259,13 +1199,10 @@ static void do_for_partition(const std::string& part, const std::string& slot,
static void do_for_partitions(const std::string& part, const std::string& slot,
const std::function<void(const std::string&)>& func, bool force_slot) {
std::string has_slot;
- // |part| can be vendor_boot:default. Query has-slot on the first token only.
- auto part_tokens = android::base::Split(part, ":");
if (slot == "all") {
- if (fb->GetVar("has-slot:" + part_tokens[0], &has_slot) != fastboot::SUCCESS) {
- die("Could not check if partition %s has slot %s", part_tokens[0].c_str(),
- slot.c_str());
+ if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
+ die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
}
if (has_slot == "yes") {
for (int i=0; i < get_slot_count(); i++) {
@@ -1279,6 +1216,11 @@ static void do_for_partitions(const std::string& part, const std::string& slot,
}
}
+static bool is_logical(const std::string& partition) {
+ std::string value;
+ return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+}
+
static bool is_retrofit_device() {
std::string value;
if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
@@ -1287,74 +1229,7 @@ static bool is_retrofit_device() {
return android::base::StartsWith(value, "system_");
}
-// Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch
-// the full image.
-static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd) {
- uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE);
- if (fetch_size == 0) {
- die("Unable to get %s. Device does not support fetch command.", FB_VAR_MAX_FETCH_SIZE);
- }
- uint64_t partition_size = get_partition_size(partition);
- if (partition_size <= 0) {
- die("Invalid partition size for partition %s: %" PRId64, partition.c_str(), partition_size);
- }
-
- uint64_t offset = 0;
- while (offset < partition_size) {
- uint64_t chunk_size = std::min(fetch_size, partition_size - offset);
- if (fb->FetchToFd(partition, fd, offset, chunk_size) != fastboot::RetCode::SUCCESS) {
- die("Unable to fetch %s (offset=%" PRIx64 ", size=%" PRIx64 ")", partition.c_str(),
- offset, chunk_size);
- }
- offset += chunk_size;
- }
- return partition_size;
-}
-
-static void do_fetch(const std::string& partition, const std::string& slot_override,
- const std::string& outfile) {
- unique_fd fd(TEMP_FAILURE_RETRY(
- open(outfile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0644)));
- auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd));
- do_for_partitions(partition, slot_override, fetch, false /* force slot */);
-}
-
-// Return immediately if not flashing a vendor boot image. If flashing a vendor boot image,
-// repack vendor_boot image with an updated ramdisk. After execution, buf is set
-// to the new image to flash, and return value is the real partition name to flash.
-static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf) {
- std::string_view pname_sv{pname};
-
- if (!android::base::StartsWith(pname_sv, "vendor_boot:") &&
- !android::base::StartsWith(pname_sv, "vendor_boot_a:") &&
- !android::base::StartsWith(pname_sv, "vendor_boot_b:")) {
- return std::string(pname_sv);
- }
- if (buf->type != FB_BUFFER_FD) {
- die("Flashing sparse vendor ramdisk image is not supported.");
- }
- if (buf->sz <= 0) {
- die("repack_ramdisk() sees negative size: %" PRId64, buf->sz);
- }
- std::string partition(pname_sv.substr(0, pname_sv.find(':')));
- std::string ramdisk(pname_sv.substr(pname_sv.find(':') + 1));
-
- unique_fd vendor_boot(make_temporary_fd("vendor boot repack"));
- uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot);
- auto repack_res = replace_vendor_ramdisk(vendor_boot, vendor_boot_size, ramdisk, buf->fd,
- static_cast<uint64_t>(buf->sz));
- if (!repack_res.ok()) {
- die("%s", repack_res.error().message().c_str());
- }
-
- buf->fd = std::move(vendor_boot);
- buf->sz = vendor_boot_size;
- buf->image_size = vendor_boot_size;
- return partition;
-}
-
static void do_flash(const char* pname, const char* fname) {
- verbose("Do flash %s %s", pname, fname);
struct fastboot_buffer buf;
if (!load_buf(fname, &buf)) {
@@ -1363,8 +1238,7 @@ static void do_flash(const char* pname, const char* fname) {
if (is_logical(pname)) {
fb->ResizePartition(pname, std::to_string(buf.image_size));
}
- std::string flash_pname = repack_ramdisk(pname, &buf);
- flash_buf(flash_pname, &buf);
+ flash_buf(pname, &buf);
}
// Sets slot_override as the active slot. If slot_override is blank,
@@ -1410,22 +1284,20 @@ static void reboot_to_userspace_fastboot() {
static void CancelSnapshotIfNeeded() {
std::string merge_status = "none";
if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&
- !merge_status.empty() && merge_status != "none") {
+ merge_status != "none") {
fb->SnapshotUpdateCommand("cancel");
}
}
class ImageSource {
public:
- virtual ~ImageSource() {};
virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
- virtual unique_fd OpenFile(const std::string& name) const = 0;
+ virtual int OpenFile(const std::string& name) const = 0;
};
class FlashAllTool {
public:
- FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
- bool wipe, bool force_flash);
+ FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe);
void Flash();
@@ -1441,19 +1313,16 @@ class FlashAllTool {
std::string slot_override_;
bool skip_secondary_;
bool wipe_;
- bool force_flash_;
std::string secondary_slot_;
std::vector<std::pair<const Image*, std::string>> boot_images_;
std::vector<std::pair<const Image*, std::string>> os_images_;
};
-FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
- bool skip_secondary, bool wipe, bool force_flash)
+FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe)
: source_(source),
slot_override_(slot_override),
skip_secondary_(skip_secondary),
- wipe_(wipe),
- force_flash_(force_flash)
+ wipe_(wipe)
{
}
@@ -1501,7 +1370,7 @@ void FlashAllTool::CheckRequirements() {
if (!source_.ReadFile("android-info.txt", &contents)) {
die("could not read android-info.txt");
}
- ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
+ ::CheckRequirements({contents.data(), contents.size()});
}
void FlashAllTool::DetermineSecondarySlot() {
@@ -1541,8 +1410,8 @@ void FlashAllTool::CollectImages() {
void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
for (const auto& [image, slot] : images) {
fastboot_buffer buf;
- unique_fd fd = source_.OpenFile(image->img_name);
- if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
+ int fd = source_.OpenFile(image->img_name);
+ if (fd < 0 || !load_buf_fd(fd, &buf)) {
if (image->optional_if_no_image) {
continue;
}
@@ -1569,7 +1438,7 @@ void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastb
}
void FlashAllTool::UpdateSuperPartition() {
- unique_fd fd = source_.OpenFile("super_empty.img");
+ int fd = source_.OpenFile("super_empty.img");
if (fd < 0) {
return;
}
@@ -1607,7 +1476,7 @@ class ZipImageSource final : public ImageSource {
public:
explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
bool ReadFile(const std::string& name, std::vector<char>* out) const override;
- unique_fd OpenFile(const std::string& name) const override;
+ int OpenFile(const std::string& name) const override;
private:
ZipArchiveHandle zip_;
@@ -1617,19 +1486,18 @@ bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) c
return UnzipToMemory(zip_, name, out);
}
-unique_fd ZipImageSource::OpenFile(const std::string& name) const {
+int ZipImageSource::OpenFile(const std::string& name) const {
return unzip_to_file(zip_, name.c_str());
}
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary,
- bool force_flash) {
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
ZipArchiveHandle zip;
int error = OpenArchive(filename, &zip);
if (error != 0) {
die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
}
- FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false, force_flash);
+ FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false);
tool.Flash();
CloseArchive(zip);
@@ -1638,7 +1506,7 @@ static void do_update(const char* filename, const std::string& slot_override, bo
class LocalImageSource final : public ImageSource {
public:
bool ReadFile(const std::string& name, std::vector<char>* out) const override;
- unique_fd OpenFile(const std::string& name) const override;
+ int OpenFile(const std::string& name) const override;
};
bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
@@ -1649,14 +1517,13 @@ bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out)
return ReadFileToVector(path, out);
}
-unique_fd LocalImageSource::OpenFile(const std::string& name) const {
+int LocalImageSource::OpenFile(const std::string& name) const {
auto path = find_item_given_name(name);
- return unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
+ return open(path.c_str(), O_RDONLY | O_BINARY);
}
-static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe,
- bool force_flash) {
- FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe, force_flash);
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
+ FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
tool.Flash();
}
@@ -1700,7 +1567,7 @@ static unsigned fb_get_flash_block_size(std::string name) {
static void fb_perform_format(
const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
- const std::string& initial_dir, const unsigned fs_options) {
+ const std::string& initial_dir) {
std::string partition_type, partition_size;
struct fastboot_buffer buf;
@@ -1763,7 +1630,7 @@ static void fb_perform_format(
logicalBlkSize = fb_get_flash_block_size("logical-block-size");
if (fs_generator_generate(gen, output.path, size, initial_dir,
- eraseBlkSize, logicalBlkSize, fs_options)) {
+ eraseBlkSize, logicalBlkSize)) {
die("Cannot generate image for %s", partition.c_str());
}
@@ -1771,7 +1638,7 @@ static void fb_perform_format(
if (fd == -1) {
die("Cannot open generated image: %s", strerror(errno));
}
- if (!load_buf_fd(std::move(fd), &buf)) {
+ if (!load_buf_fd(fd.release(), &buf)) {
die("Cannot read image: %s", strerror(errno));
}
flash_buf(partition, &buf);
@@ -1897,7 +1764,6 @@ int FastBootTool::Main(int argc, char* argv[]) {
bool skip_secondary = false;
bool set_fbe_marker = false;
bool force_flash = false;
- unsigned fs_options = 0;
int longindex;
std::string slot_override;
std::string next_active;
@@ -1915,7 +1781,6 @@ int FastBootTool::Main(int argc, char* argv[]) {
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
{"force", no_argument, 0, 0},
- {"fs-options", required_argument, 0, 0},
{"header-version", required_argument, 0, 0},
{"help", no_argument, 0, 'h'},
{"kernel-offset", required_argument, 0, 0},
@@ -1955,8 +1820,6 @@ int FastBootTool::Main(int argc, char* argv[]) {
g_disable_verity = true;
} else if (name == "force") {
force_flash = true;
- } else if (name == "fs-options") {
- fs_options = ParseFsOption(optarg);
} else if (name == "header-version") {
g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
} else if (name == "dtb") {
@@ -2066,7 +1929,6 @@ int FastBootTool::Main(int argc, char* argv[]) {
if (slot_override == "") {
std::string current_slot;
if (fb->GetVar("current-slot", &current_slot) == fastboot::SUCCESS) {
- if (current_slot[0] == '_') current_slot.erase(0, 1);
next_active = verify_slot(current_slot, false);
} else {
wants_set_active = false;
@@ -2114,7 +1976,7 @@ int FastBootTool::Main(int argc, char* argv[]) {
std::string partition = next_arg(&args);
auto format = [&](const std::string& partition) {
- fb_perform_format(partition, 0, type_override, size_override, "", fs_options);
+ fb_perform_format(partition, 0, type_override, size_override, "");
};
do_for_partitions(partition, slot_override, format, true);
} else if (command == "signature") {
@@ -2204,9 +2066,9 @@ int FastBootTool::Main(int argc, char* argv[]) {
} else if (command == "flashall") {
if (slot_override == "all") {
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
- do_flashall(slot_override, true, wants_wipe, force_flash);
+ do_flashall(slot_override, true, wants_wipe);
} else {
- do_flashall(slot_override, skip_secondary, wants_wipe, force_flash);
+ do_flashall(slot_override, skip_secondary, wants_wipe);
}
wants_reboot = true;
} else if (command == "update") {
@@ -2218,7 +2080,7 @@ int FastBootTool::Main(int argc, char* argv[]) {
if (!args.empty()) {
filename = next_arg(&args);
}
- do_update(filename.c_str(), slot_override, skip_secondary || slot_all, force_flash);
+ do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
wants_reboot = true;
} else if (command == FB_CMD_SET_ACTIVE) {
std::string slot = verify_slot(next_arg(&args), false);
@@ -2230,7 +2092,7 @@ int FastBootTool::Main(int argc, char* argv[]) {
if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
die("cannot load '%s'", filename.c_str());
}
- fb->Download(filename, buf.fd.get(), buf.sz);
+ fb->Download(filename, buf.fd, buf.sz);
} else if (command == "get_staged") {
std::string filename = next_arg(&args);
fb->Upload(filename);
@@ -2284,10 +2146,6 @@ int FastBootTool::Main(int argc, char* argv[]) {
syntax_error("expected: snapshot-update [cancel|merge]");
}
fb->SnapshotUpdateCommand(arg);
- } else if (command == FB_CMD_FETCH) {
- std::string partition = next_arg(&args);
- std::string outfile = next_arg(&args);
- do_fetch(partition, slot_override, outfile);
} else {
syntax_error("unknown command %s", command.c_str());
}
@@ -2308,10 +2166,10 @@ int FastBootTool::Main(int argc, char* argv[]) {
if (partition == "userdata" && set_fbe_marker) {
fprintf(stderr, "setting FBE marker on initial userdata...\n");
std::string initial_userdata_dir = create_fbemarker_tmpdir();
- fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir, fs_options);
+ fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir);
delete_fbemarker_tmpdir(initial_userdata_dir);
} else {
- fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+ fb_perform_format(partition, 1, partition_type, "", "");
}
}
}
@@ -2361,23 +2219,3 @@ void FastBootTool::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
}
hdr->SetOsVersion(major, minor, patch);
}
-
-unsigned FastBootTool::ParseFsOption(const char* arg) {
- unsigned fsOptions = 0;
-
- std::vector<std::string> options = android::base::Split(arg, ",");
- if (options.size() < 1)
- syntax_error("bad options: %s", arg);
-
- for (size_t i = 0; i < options.size(); ++i) {
- if (options[i] == "casefold")
- fsOptions |= (1 << FS_OPT_CASEFOLD);
- else if (options[i] == "projid")
- fsOptions |= (1 << FS_OPT_PROJID);
- else if (options[i] == "compress")
- fsOptions |= (1 << FS_OPT_COMPRESS);
- else
- syntax_error("unsupported options: %s", options[i].c_str());
- }
- return fsOptions;
-}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index c23793a9c..9f1825366 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -34,5 +34,4 @@ class FastBootTool {
void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
void ParseOsVersion(boot_img_hdr_v1*, const char*);
- unsigned ParseFsOption(const char*);
};
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 99a48734c..8d534ea32 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -30,7 +30,6 @@
#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -43,17 +42,14 @@
#include <android-base/file.h>
#include <android-base/mapped_file.h>
-#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <storage_literals/storage_literals.h>
#include "constants.h"
#include "transport.h"
using android::base::StringPrintf;
-using namespace android::storage_literals;
namespace fastboot {
@@ -144,8 +140,7 @@ RetCode FastBootDriver::FlashPartition(const std::string& partition,
return Flash(partition);
}
-RetCode FastBootDriver::FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
- uint32_t size) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
RetCode ret;
if ((ret = Download(partition, fd, size))) {
return ret;
@@ -183,16 +178,15 @@ RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>
return SUCCESS;
}
-RetCode FastBootDriver::Download(const std::string& name, android::base::borrowed_fd fd,
- size_t size, std::string* response,
- std::vector<std::string>* info) {
+RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
+ std::string* response, std::vector<std::string>* info) {
prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
auto result = Download(fd, size, response, info);
epilog_(result);
return result;
}
-RetCode FastBootDriver::Download(android::base::borrowed_fd fd, size_t size, std::string* response,
+RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
std::vector<std::string>* info) {
RetCode ret;
@@ -303,85 +297,41 @@ RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response
return result;
}
-// This function executes cmd, then expect a "DATA" response with a number N, followed
-// by N bytes, and another response.
-// This is the common way for the device to send data to the driver used by upload and fetch.
-RetCode FastBootDriver::RunAndReadBuffer(
- const std::string& cmd, std::string* response, std::vector<std::string>* info,
- const std::function<RetCode(const char* data, uint64_t size)>& write_fn) {
+RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
+ std::vector<std::string>* info) {
RetCode ret;
int dsize = 0;
- if ((ret = RawCommand(cmd, response, info, &dsize))) {
- error_ = android::base::StringPrintf("%s request failed: %s", cmd.c_str(), error_.c_str());
+ if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
+ error_ = "Upload request failed: " + error_;
return ret;
}
- if (dsize <= 0) {
- error_ = android::base::StringPrintf("%s request failed, device reports %d bytes available",
- cmd.c_str(), dsize);
+ if (!dsize) {
+ error_ = "Upload request failed, device reports 0 bytes available";
return BAD_DEV_RESP;
}
- const uint64_t total_size = dsize;
- const uint64_t buf_size = std::min<uint64_t>(total_size, 1_MiB);
- std::vector<char> data(buf_size);
- uint64_t current_offset = 0;
- while (current_offset < total_size) {
- uint64_t remaining = total_size - current_offset;
- uint64_t chunk_size = std::min(buf_size, remaining);
- if ((ret = ReadBuffer(data.data(), chunk_size)) != SUCCESS) {
- return ret;
- }
- if ((ret = write_fn(data.data(), chunk_size)) != SUCCESS) {
- return ret;
- }
- current_offset += chunk_size;
+ std::vector<char> data;
+ data.resize(dsize);
+
+ if ((ret = ReadBuffer(data))) {
+ return ret;
}
- return HandleResponse(response, info);
-}
-RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
- std::vector<std::string>* info) {
std::ofstream ofs;
ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
if (ofs.fail()) {
error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
return IO_ERROR;
}
- auto write_fn = [&](const char* data, uint64_t size) {
- ofs.write(data, size);
- if (ofs.fail() || ofs.bad()) {
- error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
- return IO_ERROR;
- }
- return SUCCESS;
- };
- RetCode ret = RunAndReadBuffer(FB_CMD_UPLOAD, response, info, write_fn);
+ ofs.write(data.data(), data.size());
+ if (ofs.fail() || ofs.bad()) {
+ error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
+ return IO_ERROR;
+ }
ofs.close();
- return ret;
-}
-RetCode FastBootDriver::FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
- int64_t offset, int64_t size, std::string* response,
- std::vector<std::string>* info) {
- prolog_(android::base::StringPrintf("Fetching %s (offset=%" PRIx64 ", size=%" PRIx64 ")",
- partition.c_str(), offset, size));
- std::string cmd = FB_CMD_FETCH ":" + partition;
- if (offset >= 0) {
- cmd += android::base::StringPrintf(":0x%08" PRIx64, offset);
- if (size >= 0) {
- cmd += android::base::StringPrintf(":0x%08" PRIx64, size);
- }
- }
- RetCode ret = RunAndReadBuffer(cmd, response, info, [&](const char* data, uint64_t size) {
- if (!android::base::WriteFully(fd, data, size)) {
- error_ = android::base::StringPrintf("Cannot write: %s", strerror(errno));
- return IO_ERROR;
- }
- return SUCCESS;
- });
- epilog_(ret);
- return ret;
+ return HandleResponse(response, info);
}
// Helpers
@@ -523,7 +473,7 @@ std::string FastBootDriver::ErrnoStr(const std::string& msg) {
}
/******************************* PRIVATE **************************************/
-RetCode FastBootDriver::SendBuffer(android::base::borrowed_fd fd, size_t size) {
+RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
off64_t offset = 0;
uint32_t remaining = size;
@@ -574,6 +524,11 @@ RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
return SUCCESS;
}
+RetCode FastBootDriver::ReadBuffer(std::vector<char>& buf) {
+ // Read the buffer
+ return ReadBuffer(buf.data(), buf.size());
+}
+
RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
// Read the buffer
ssize_t tmp = transport_->Read(buf, size);
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index bccd6685f..72656322e 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -34,7 +34,6 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
#include <bootimg.h>
#include <inttypes.h>
#include <sparse/sparse.h>
@@ -77,9 +76,9 @@ class FastBootDriver {
RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode CreatePartition(const std::string& partition, const std::string& size);
RetCode DeletePartition(const std::string& partition);
- RetCode Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
- std::string* response = nullptr, std::vector<std::string>* info = nullptr);
- RetCode Download(android::base::borrowed_fd fd, size_t size, std::string* response = nullptr,
+ RetCode Download(const std::string& name, int fd, size_t size, std::string* response = nullptr,
+ std::vector<std::string>* info = nullptr);
+ RetCode Download(int fd, size_t size, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Download(const std::string& name, const std::vector<char>& buf,
std::string* response = nullptr, std::vector<std::string>* info = nullptr);
@@ -107,14 +106,10 @@ class FastBootDriver {
std::vector<std::string>* info = nullptr);
RetCode SnapshotUpdateCommand(const std::string& command, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
- RetCode FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
- int64_t offset = -1, int64_t size = -1, std::string* response = nullptr,
- std::vector<std::string>* info = nullptr);
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
- RetCode FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
- uint32_t sz);
+ RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
size_t current, size_t total);
@@ -150,17 +145,15 @@ class FastBootDriver {
Transport* transport_;
private:
- RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
+ RetCode SendBuffer(int fd, size_t size);
RetCode SendBuffer(const std::vector<char>& buf);
RetCode SendBuffer(const void* buf, size_t size);
+ RetCode ReadBuffer(std::vector<char>& buf);
RetCode ReadBuffer(void* buf, size_t size);
RetCode UploadInner(const std::string& outfile, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
- RetCode RunAndReadBuffer(const std::string& cmd, std::string* response,
- std::vector<std::string>* info,
- const std::function<RetCode(const char*, uint64_t)>& write_fn);
int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 458a7a184..8c0aa6bf4 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -113,7 +113,7 @@ static int exec_cmd(const char* path, const char** argv, const char** envp) {
static int generate_ext4_image(const char* fileName, long long partSize,
const std::string& initial_dir, unsigned eraseBlkSize,
- unsigned logicalBlkSize, const unsigned fsOptions) {
+ unsigned logicalBlkSize) {
static constexpr int block_size = 4096;
const std::string exec_dir = android::base::GetExecutableDirectory();
@@ -137,12 +137,6 @@ static int generate_ext4_image(const char* fileName, long long partSize,
mke2fs_args.push_back(ext_attr.c_str());
mke2fs_args.push_back("-O");
mke2fs_args.push_back("uninit_bg");
-
- if (fsOptions & (1 << FS_OPT_PROJID)) {
- mke2fs_args.push_back("-I");
- mke2fs_args.push_back("512");
- }
-
mke2fs_args.push_back(fileName);
std::string size_str = std::to_string(partSize / block_size);
@@ -168,22 +162,9 @@ static int generate_ext4_image(const char* fileName, long long partSize,
return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
}
-enum {
- // clang-format off
- FSCK_SUCCESS = 0,
- FSCK_ERROR_CORRECTED = 1 << 0,
- FSCK_SYSTEM_SHOULD_REBOOT = 1 << 1,
- FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
- FSCK_OPERATIONAL_ERROR = 1 << 3,
- FSCK_USAGE_OR_SYNTAX_ERROR = 1 << 4,
- FSCK_USER_CANCELLED = 1 << 5,
- FSCK_SHARED_LIB_ERROR = 1 << 7,
- // clang-format on
-};
-
-static int generate_f2fs_image(const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned /* unused */,
- unsigned /* unused */, const unsigned fsOptions) {
+static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
+ unsigned /* unused */, unsigned /* unused */)
+{
const std::string exec_dir = android::base::GetExecutableDirectory();
const std::string mkf2fs_path = exec_dir + "/make_f2fs";
std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()};
@@ -193,26 +174,6 @@ static int generate_f2fs_image(const char* fileName, long long partSize,
mkf2fs_args.push_back(size_str.c_str());
mkf2fs_args.push_back("-g");
mkf2fs_args.push_back("android");
-
- if (fsOptions & (1 << FS_OPT_PROJID)) {
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("project_quota,extra_attr");
- }
-
- if (fsOptions & (1 << FS_OPT_CASEFOLD)) {
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("casefold");
- mkf2fs_args.push_back("-C");
- mkf2fs_args.push_back("utf8");
- }
-
- if (fsOptions & (1 << FS_OPT_COMPRESS)) {
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("compression");
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("extra_attr");
- }
-
mkf2fs_args.push_back(fileName);
mkf2fs_args.push_back(nullptr);
@@ -229,11 +190,7 @@ static int generate_f2fs_image(const char* fileName, long long partSize,
std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
"-f", initial_dir.c_str(), fileName, nullptr};
- ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
- if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
- return -1;
- }
- return 0;
+ return exec_cmd(sload_args[0], sload_args.data(), nullptr);
}
static const struct fs_generator {
@@ -241,7 +198,7 @@ static const struct fs_generator {
//returns 0 or error value
int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
- unsigned eraseBlkSize, unsigned logicalBlkSize, const unsigned fsOptions);
+ unsigned eraseBlkSize, unsigned logicalBlkSize);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -258,7 +215,7 @@ const struct fs_generator* fs_get_generator(const std::string& fs_type) {
}
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize,
- unsigned logicalBlkSize, const unsigned fsOptions) {
- return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize, fsOptions);
+ const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
+{
+ return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index f832938e6..331100de1 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -5,13 +5,6 @@
struct fs_generator;
-enum FS_OPTION {
- FS_OPT_CASEFOLD,
- FS_OPT_PROJID,
- FS_OPT_COMPRESS,
-};
-
const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
- const std::string& initial_dir, unsigned eraseBlkSize = 0,
- unsigned logicalBlkSize = 0, unsigned fsOptions = 0);
+ const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 159c314a9..aa449b2fb 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -1,12 +1,3 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_core_fastboot_license"
- // to get the below license kinds:
- // SPDX-license-identifier-BSD
- default_applicable_licenses: ["system_core_fastboot_license"],
-}
-
cc_test_host {
name: "fuzzy_fastboot",
compile_multilib: "first",
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index b6beaf93a..e7f785b83 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -43,10 +43,8 @@
#include <thread>
#include <vector>
-#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
#include <gtest/gtest.h>
#include <sparse/sparse.h>
@@ -351,35 +349,22 @@ TEST_F(Conformance, GetVarBattVoltageOk) {
EXPECT_TRUE(var == "yes" || var == "no") << "getvar:battery-soc-ok must be 'yes' or 'no'";
}
-void AssertHexUint32(const std::string& name, const std::string& var) {
- ASSERT_NE(var, "") << "getvar:" << name << " responded with empty string";
- // This must start with 0x
- ASSERT_FALSE(isspace(var.front()))
- << "getvar:" << name << " responded with a string with leading whitespace";
- ASSERT_FALSE(var.compare(0, 2, "0x"))
- << "getvar:" << name << " responded with a string that does not start with 0x...";
- int64_t size = strtoll(var.c_str(), nullptr, 16);
- ASSERT_GT(size, 0) << "'" + var + "' is not a valid response from getvar:" << name;
- // At most 32-bits
- ASSERT_LE(size, std::numeric_limits<uint32_t>::max())
- << "getvar:" << name << " must fit in a uint32_t";
- ASSERT_LE(var.size(), FB_RESPONSE_SZ - 4)
- << "getvar:" << name << " responded with too large of string: " + var;
-}
-
TEST_F(Conformance, GetVarDownloadSize) {
std::string var;
EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
- AssertHexUint32("max-download-size", var);
-}
-
-// If fetch is supported, getvar:max-fetch-size must return a hex string.
-TEST_F(Conformance, GetVarFetchSize) {
- std::string var;
- if (SUCCESS != fb->GetVar("max-fetch-size", &var)) {
- GTEST_SKIP() << "getvar:max-fetch-size failed";
- }
- AssertHexUint32("max-fetch-size", var);
+ EXPECT_NE(var, "") << "getvar:max-download-size responded with empty string";
+ // This must start with 0x
+ EXPECT_FALSE(isspace(var.front()))
+ << "getvar:max-download-size responded with a string with leading whitespace";
+ EXPECT_FALSE(var.compare(0, 2, "0x"))
+ << "getvar:max-download-size responded with a string that does not start with 0x...";
+ int64_t size = strtoll(var.c_str(), nullptr, 16);
+ EXPECT_GT(size, 0) << "'" + var + "' is not a valid response from getvar:max-download-size";
+ // At most 32-bits
+ EXPECT_LE(size, std::numeric_limits<uint32_t>::max())
+ << "getvar:max-download-size must fit in a uint32_t";
+ EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)
+ << "getvar:max-download-size responded with too large of string: " + var;
}
TEST_F(Conformance, GetVarAll) {
@@ -671,33 +656,6 @@ TEST_F(UnlockPermissions, DownloadFlash) {
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in unlocked mode";
}
-// If the implementation supports getvar:max-fetch-size, it must also support fetch:vendor_boot*.
-TEST_F(UnlockPermissions, FetchVendorBoot) {
- std::string var;
- uint64_t fetch_size;
- if (fb->GetVar("max-fetch-size", &var) != SUCCESS) {
- GTEST_SKIP() << "This test is skipped because fetch is not supported.";
- }
- ASSERT_FALSE(var.empty());
- ASSERT_TRUE(android::base::ParseUint(var, &fetch_size)) << var << " is not an integer";
- std::vector<std::tuple<std::string, uint64_t>> parts;
- EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
- for (const auto& [partition, partition_size] : parts) {
- if (!android::base::StartsWith(partition, "vendor_boot")) continue;
- TemporaryFile fetched;
-
- uint64_t offset = 0;
- while (offset < partition_size) {
- uint64_t chunk_size = std::min(fetch_size, partition_size - offset);
- auto ret = fb->FetchToFd(partition, fetched.fd, offset, chunk_size);
- ASSERT_EQ(fastboot::RetCode::SUCCESS, ret)
- << "Unable to fetch " << partition << " (offset=" << offset
- << ", size=" << chunk_size << ")";
- offset += chunk_size;
- }
- }
-}
-
TEST_F(LockPermissions, DownloadFlash) {
std::vector<char> buf{'a', 'o', 's', 'p'};
EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in locked mode";
@@ -759,16 +717,6 @@ TEST_F(LockPermissions, Boot) {
EXPECT_GT(resp.size(), 0) << "No error message was returned by device after FAIL";
}
-TEST_F(LockPermissions, FetchVendorBoot) {
- std::vector<std::tuple<std::string, uint64_t>> parts;
- EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
- for (const auto& [partition, _] : parts) {
- TemporaryFile fetched;
- ASSERT_EQ(fb->FetchToFd(partition, fetched.fd, 0, 0), DEVICE_FAIL)
- << "fetch:" << partition << ":0:0 did not fail in locked mode";
- }
-}
-
TEST_F(Fuzz, DownloadSize) {
std::string var;
EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
@@ -1338,7 +1286,7 @@ TEST_P(UserdataPartition, UnlockErases) {
ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
- // Validity check of hash
+ // Sanity check of hash
EXPECT_NE(hash_before, hash_buf)
<< "Writing a random buffer to 'userdata' had the same hash as after erasing it";
SetLockState(true); // Lock the device
diff --git a/fastboot/testdata/Android.bp b/fastboot/testdata/Android.bp
deleted file mode 100644
index a490fe270..000000000
--- a/fastboot/testdata/Android.bp
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-python_binary_host {
- name: "fastboot_gen_rand",
- visibility: [":__subpackages__"],
- srcs: ["fastboot_gen_rand.py"],
-}
-
-genrule_defaults {
- name: "fastboot_test_data_gen_defaults",
- visibility: ["//system/core/fastboot"],
- tools: [
- "fastboot_gen_rand",
- ],
-}
-
-// Genrules for components of test vendor boot image.
-
-// Fake dtb image.
-genrule {
- name: "fastboot_test_dtb",
- defaults: ["fastboot_test_data_gen_defaults"],
- out: ["test_dtb.img"],
- cmd: "$(location fastboot_gen_rand) --seed dtb --length 1024 > $(out)",
-}
-
-// Fake bootconfig image.
-genrule {
- name: "fastboot_test_bootconfig",
- defaults: ["fastboot_test_data_gen_defaults"],
- out: ["test_bootconfig.img"],
- cmd: "$(location fastboot_gen_rand) --seed bootconfig --length 1024 > $(out)",
-}
-
-// Fake vendor ramdisk with type "none".
-genrule {
- name: "fastboot_test_vendor_ramdisk_none",
- defaults: ["fastboot_test_data_gen_defaults"],
- out: ["test_vendor_ramdisk_none.img"],
- cmd: "$(location fastboot_gen_rand) --seed vendor_ramdisk_none --length 1024 > $(out)",
-}
-
-// Fake vendor ramdisk with type "platform".
-genrule {
- name: "fastboot_test_vendor_ramdisk_platform",
- defaults: ["fastboot_test_data_gen_defaults"],
- out: ["test_vendor_ramdisk_platform.img"],
- cmd: "$(location fastboot_gen_rand) --seed vendor_ramdisk_platform --length 1024 > $(out)",
-}
-
-// Fake replacement ramdisk.
-genrule {
- name: "fastboot_test_vendor_ramdisk_replace",
- defaults: ["fastboot_test_data_gen_defaults"],
- out: ["test_vendor_ramdisk_replace.img"],
- cmd: "$(location fastboot_gen_rand) --seed replace --length 3072 > $(out)",
-}
-
-// Genrules for test vendor boot images.
-
-fastboot_sign_test_image = "$(location avbtool) add_hash_footer --salt 00 --image $(out) " +
- "--partition_name vendor_boot --partition_size $$(( 1 * 1024 * 1024 ))"
-
-genrule_defaults {
- name: "fastboot_test_vendor_boot_gen_defaults",
- defaults: ["fastboot_test_data_gen_defaults"],
- tools: [
- "avbtool",
- "mkbootimg",
- ],
-}
-
-genrule {
- name: "fastboot_test_vendor_boot_v3",
- defaults: ["fastboot_test_vendor_boot_gen_defaults"],
- out: ["vendor_boot_v3.img"],
- srcs: [
- ":fastboot_test_dtb",
- ":fastboot_test_vendor_ramdisk_none",
- ],
- cmd: "$(location mkbootimg) --header_version 3 " +
- "--vendor_ramdisk $(location :fastboot_test_vendor_ramdisk_none) " +
- "--dtb $(location :fastboot_test_dtb) " +
- "--vendor_boot $(out) && " +
- fastboot_sign_test_image,
-}
-
-genrule {
- name: "fastboot_test_vendor_boot_v4_without_frag",
- defaults: ["fastboot_test_vendor_boot_gen_defaults"],
- out: ["vendor_boot_v4_without_frag.img"],
- srcs: [
- ":fastboot_test_dtb",
- ":fastboot_test_vendor_ramdisk_none",
- ":fastboot_test_bootconfig",
- ],
- cmd: "$(location mkbootimg) --header_version 4 " +
- "--vendor_ramdisk $(location :fastboot_test_vendor_ramdisk_none) " +
- "--dtb $(location :fastboot_test_dtb) " +
- "--vendor_bootconfig $(location :fastboot_test_bootconfig) " +
- "--vendor_boot $(out) && " +
- fastboot_sign_test_image,
-}
-
-genrule {
- name: "fastboot_test_vendor_boot_v4_with_frag",
- defaults: ["fastboot_test_vendor_boot_gen_defaults"],
- out: ["vendor_boot_v4_with_frag.img"],
- srcs: [
- ":fastboot_test_dtb",
- ":fastboot_test_vendor_ramdisk_none",
- ":fastboot_test_vendor_ramdisk_platform",
- ":fastboot_test_bootconfig",
- ],
- cmd: "$(location mkbootimg) --header_version 4 " +
- "--dtb $(location :fastboot_test_dtb) " +
- "--vendor_bootconfig $(location :fastboot_test_bootconfig) " +
- "--ramdisk_type none --ramdisk_name none_ramdisk " +
- "--vendor_ramdisk_fragment $(location :fastboot_test_vendor_ramdisk_none) " +
- "--ramdisk_type platform --ramdisk_name platform_ramdisk " +
- "--vendor_ramdisk_fragment $(location :fastboot_test_vendor_ramdisk_platform) " +
- "--vendor_boot $(out) && " +
- fastboot_sign_test_image,
-}
diff --git a/fastboot/testdata/fastboot_gen_rand.py b/fastboot/testdata/fastboot_gen_rand.py
deleted file mode 100644
index a87467b88..000000000
--- a/fastboot/testdata/fastboot_gen_rand.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Write given number of random bytes, generated with optional seed.
-"""
-
-import random, argparse
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('--seed', help='Seed to random generator')
- parser.add_argument('--length', type=int, required=True, help='Length of output')
- args = parser.parse_args()
-
- if args.seed:
- random.seed(args.seed)
-
- print(''.join(chr(random.randrange(0,0xff)) for _ in range(args.length)))
diff --git a/fastboot/usb.h b/fastboot/usb.h
index e5f56e20a..7ca44c414 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -50,8 +50,6 @@ struct usb_ifc_info {
char serial_number[256];
char device_path[256];
-
- char interface[256];
};
class UsbTransport : public Transport {
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 964488cc8..6363aa547 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -43,8 +43,6 @@
#include <linux/version.h>
#include <linux/usb/ch9.h>
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
#include <chrono>
#include <memory>
#include <thread>
@@ -265,13 +263,6 @@ static int filter_usb_device(char* sysfs_name,
info.has_bulk_in = (in != -1);
info.has_bulk_out = (out != -1);
- std::string interface;
- auto path = android::base::StringPrintf("/sys/bus/usb/devices/%s/%s:1.%d/interface",
- sysfs_name, sysfs_name, ifc->bInterfaceNumber);
- if (android::base::ReadFileToString(path, &interface)) {
- snprintf(info.interface, sizeof(info.interface), "%s", interface.c_str());
- }
-
if(callback(&info) == 0) {
*ept_in_id = in;
*ept_out_id = out;
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 610eebfb2..8a3c21331 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -368,7 +368,6 @@ static int try_device(io_service_t device, usb_handle *handle) {
// device has no serial number
handle->info.serial_number[0] = 0;
}
- handle->info.interface[0] = 0;
handle->info.writable = 1;
if (try_interfaces(dev, handle)) {
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 67bf8a30b..bf840f888 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -319,7 +319,6 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) {
&serial_number_len, true)) {
info.serial_number[0] = 0;
}
- info.interface[0] = 0;
info.device_path[0] = 0;
diff --git a/fastboot/vendor_boot_img_utils.cpp b/fastboot/vendor_boot_img_utils.cpp
deleted file mode 100644
index 9e09abbd1..000000000
--- a/fastboot/vendor_boot_img_utils.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "vendor_boot_img_utils.h"
-
-#include <string.h>
-
-#include <android-base/file.h>
-#include <android-base/result.h>
-#include <bootimg.h>
-#include <libavb/libavb.h>
-
-namespace {
-
-using android::base::Result;
-
-// Updates a given buffer by creating a new one.
-class DataUpdater {
- public:
- DataUpdater(const std::string& old_data) : old_data_(&old_data) {
- old_data_ptr_ = old_data_->data();
- new_data_.resize(old_data_->size(), '\0');
- new_data_ptr_ = new_data_.data();
- }
- // Copy |num_bytes| from src to dst.
- [[nodiscard]] Result<void> Copy(uint32_t num_bytes) {
- if (num_bytes == 0) return {};
- if (auto res = CheckAdvance(old_data_ptr_, old_end(), num_bytes, __FUNCTION__); !res.ok())
- return res;
- if (auto res = CheckAdvance(new_data_ptr_, new_end(), num_bytes, __FUNCTION__); !res.ok())
- return res;
- memcpy(new_data_ptr_, old_data_ptr_, num_bytes);
- old_data_ptr_ += num_bytes;
- new_data_ptr_ += num_bytes;
- return {};
- }
- // Replace |old_num_bytes| from src with new data.
- [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const std::string& new_data) {
- return Replace(old_num_bytes, new_data.data(), new_data.size());
- }
- [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const void* new_data,
- uint32_t new_data_size) {
- if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_num_bytes, __FUNCTION__);
- !res.ok())
- return res;
- old_data_ptr_ += old_num_bytes;
-
- if (new_data_size == 0) return {};
- if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_data_size, __FUNCTION__);
- !res.ok())
- return res;
- memcpy(new_data_ptr_, new_data, new_data_size);
- new_data_ptr_ += new_data_size;
- return {};
- }
- // Skip |old_skip| from src and |new_skip| from dst, respectively.
- [[nodiscard]] Result<void> Skip(uint32_t old_skip, uint32_t new_skip) {
- if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_skip, __FUNCTION__); !res.ok())
- return res;
- old_data_ptr_ += old_skip;
- if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_skip, __FUNCTION__); !res.ok())
- return res;
- new_data_ptr_ += new_skip;
- return {};
- }
-
- [[nodiscard]] Result<void> Seek(uint32_t offset) {
- if (offset > size()) return Errorf("Cannot seek 0x{:x}, size is 0x{:x}", offset, size());
- old_data_ptr_ = old_begin() + offset;
- new_data_ptr_ = new_begin() + offset;
- return {};
- }
-
- std::string Finish() {
- new_data_ptr_ = nullptr;
- return std::move(new_data_);
- }
-
- [[nodiscard]] Result<void> CheckOffset(uint32_t old_offset, uint32_t new_offset) {
- if (old_begin() + old_offset != old_cur())
- return Errorf("Old offset mismatch: expected: 0x{:x}, actual: 0x{:x}", old_offset,
- old_cur() - old_begin());
- if (new_begin() + new_offset != new_cur())
- return Errorf("New offset mismatch: expected: 0x{:x}, actual: 0x{:x}", new_offset,
- new_cur() - new_begin());
- return {};
- }
-
- uint64_t size() const { return old_data_->size(); }
- const char* old_begin() const { return old_data_->data(); }
- const char* old_cur() { return old_data_ptr_; }
- const char* old_end() const { return old_data_->data() + old_data_->size(); }
- char* new_begin() { return new_data_.data(); }
- char* new_cur() { return new_data_ptr_; }
- char* new_end() { return new_data_.data() + new_data_.size(); }
-
- private:
- // Check if it is okay to advance |num_bytes| from |current|.
- [[nodiscard]] Result<void> CheckAdvance(const char* current, const char* end,
- uint32_t num_bytes, const char* op) {
- auto new_end = current + num_bytes;
- if (new_end < current /* add overflow */)
- return Errorf("{}: Addition overflow: 0x{} + 0x{:x} < 0x{}", op, fmt::ptr(current),
- num_bytes, fmt::ptr(current));
- if (new_end > end)
- return Errorf("{}: Boundary overflow: 0x{} + 0x{:x} > 0x{}", op, fmt::ptr(current),
- num_bytes, fmt::ptr(end));
- return {};
- }
- const std::string* old_data_;
- std::string new_data_;
- const char* old_data_ptr_;
- char* new_data_ptr_;
-};
-
-// Get the size of vendor boot header.
-[[nodiscard]] Result<uint32_t> get_vendor_boot_header_size(const vendor_boot_img_hdr_v3* hdr) {
- if (hdr->header_version == 3) return sizeof(vendor_boot_img_hdr_v3);
- if (hdr->header_version == 4) return sizeof(vendor_boot_img_hdr_v4);
- return Errorf("Unrecognized vendor boot header version {}", hdr->header_version);
-}
-
-// Check that content contains a valid vendor boot image header with a version at least |version|.
-[[nodiscard]] Result<void> check_vendor_boot_hdr(const std::string& content, uint32_t version) {
- // get_vendor_boot_header_size reads header_version, so make sure reading it does not
- // go out of bounds by ensuring that the content has at least the size of V3 header.
- if (content.size() < sizeof(vendor_boot_img_hdr_v3)) {
- return Errorf("Size of vendor boot is 0x{:x}, less than size of V3 header: 0x{:x}",
- content.size(), sizeof(vendor_boot_img_hdr_v3));
- }
- // Now read hdr->header_version and assert the size.
- auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(content.data());
- auto expect_header_size = get_vendor_boot_header_size(hdr);
- if (!expect_header_size.ok()) return expect_header_size.error();
- if (content.size() < *expect_header_size) {
- return Errorf("Size of vendor boot is 0x{:x}, less than size of V{} header: 0x{:x}",
- content.size(), version, *expect_header_size);
- }
- if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {
- return Errorf("Vendor boot image magic mismatch");
- }
- if (hdr->header_version < version) {
- return Errorf("Require vendor boot header V{} but is V{}", version, hdr->header_version);
- }
- return {};
-}
-
-// Wrapper of ReadFdToString. Seek to the beginning and read the whole file to string.
-[[nodiscard]] Result<std::string> load_file(android::base::borrowed_fd fd, uint64_t expected_size,
- const char* what) {
- if (lseek(fd.get(), 0, SEEK_SET) != 0) {
- return ErrnoErrorf("Can't seek to the beginning of {} image", what);
- }
- std::string content;
- if (!android::base::ReadFdToString(fd, &content)) {
- return ErrnoErrorf("Cannot read {} to string", what);
- }
- if (content.size() != expected_size) {
- return Errorf("Size of {} does not match, expected 0x{:x}, read 0x{:x}", what,
- expected_size, content.size());
- }
- return content;
-}
-
-// Wrapper of WriteStringToFd. Seek to the beginning and write the whole file to string.
-[[nodiscard]] Result<void> store_file(android::base::borrowed_fd fd, const std::string& data,
- const char* what) {
- if (lseek(fd.get(), 0, SEEK_SET) != 0) {
- return ErrnoErrorf("Cannot seek to beginning of {} before writing", what);
- }
- if (!android::base::WriteStringToFd(data, fd)) {
- return ErrnoErrorf("Cannot write new content to {}", what);
- }
- if (TEMP_FAILURE_RETRY(ftruncate(fd.get(), data.size())) == -1) {
- return ErrnoErrorf("Truncating new vendor boot image to 0x{:x} fails", data.size());
- }
- return {};
-}
-
-// Copy AVB footer if it exists in the old buffer.
-[[nodiscard]] Result<void> copy_avb_footer(DataUpdater* updater) {
- if (updater->size() < AVB_FOOTER_SIZE) return {};
- if (auto res = updater->Seek(updater->size() - AVB_FOOTER_SIZE); !res.ok()) return res;
- if (memcmp(updater->old_cur(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) return {};
- return updater->Copy(AVB_FOOTER_SIZE);
-}
-
-// round |value| up to a multiple of |page_size|.
-inline uint32_t round_up(uint32_t value, uint32_t page_size) {
- return (value + page_size - 1) / page_size * page_size;
-}
-
-// Replace the vendor ramdisk as a whole.
-[[nodiscard]] Result<std::string> replace_default_vendor_ramdisk(const std::string& vendor_boot,
- const std::string& new_ramdisk) {
- if (auto res = check_vendor_boot_hdr(vendor_boot, 3); !res.ok()) return res.error();
- auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(vendor_boot.data());
- auto hdr_size = get_vendor_boot_header_size(hdr);
- if (!hdr_size.ok()) return hdr_size.error();
- // Refer to bootimg.h for details. Numbers are in bytes.
- const uint32_t o = round_up(*hdr_size, hdr->page_size);
- const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
- const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);
-
- DataUpdater updater(vendor_boot);
-
- // Copy header (O bytes), then update fields in header.
- if (auto res = updater.Copy(o); !res.ok()) return res.error();
- auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v3*>(updater.new_begin());
- new_hdr->vendor_ramdisk_size = new_ramdisk.size();
- // Because it is unknown how the new ramdisk is fragmented, the whole table is replaced
- // with a single entry representing the full ramdisk.
- if (new_hdr->header_version >= 4) {
- auto new_hdr_v4 = static_cast<vendor_boot_img_hdr_v4*>(new_hdr);
- new_hdr_v4->vendor_ramdisk_table_entry_size = sizeof(vendor_ramdisk_table_entry_v4);
- new_hdr_v4->vendor_ramdisk_table_entry_num = 1;
- new_hdr_v4->vendor_ramdisk_table_size = new_hdr_v4->vendor_ramdisk_table_entry_num *
- new_hdr_v4->vendor_ramdisk_table_entry_size;
- }
-
- // Copy the new ramdisk.
- if (auto res = updater.Replace(hdr->vendor_ramdisk_size, new_ramdisk); !res.ok())
- return res.error();
- const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);
- if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);
- !res.ok())
- return res.error();
- if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();
-
- // Copy DTB (Q bytes).
- if (auto res = updater.Copy(q); !res.ok()) return res.error();
-
- if (new_hdr->header_version >= 4) {
- auto hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(hdr);
- const uint32_t r = round_up(hdr_v4->vendor_ramdisk_table_size, hdr_v4->page_size);
- const uint32_t s = round_up(hdr_v4->bootconfig_size, hdr_v4->page_size);
-
- auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());
- auto new_hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(new_hdr);
- auto new_r = round_up(new_hdr_v4->vendor_ramdisk_table_size, new_hdr->page_size);
- if (auto res = updater.Skip(r, new_r); !res.ok()) return res.error();
- if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + q + new_r); !res.ok())
- return res.error();
-
- // Replace table with single entry representing the full ramdisk.
- new_entry->ramdisk_size = new_hdr->vendor_ramdisk_size;
- new_entry->ramdisk_offset = 0;
- new_entry->ramdisk_type = VENDOR_RAMDISK_TYPE_NONE;
- memset(new_entry->ramdisk_name, '\0', VENDOR_RAMDISK_NAME_SIZE);
- memset(new_entry->board_id, '\0', VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);
-
- // Copy bootconfig (S bytes).
- if (auto res = updater.Copy(s); !res.ok()) return res.error();
- }
-
- if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();
- return updater.Finish();
-}
-
-// Find a ramdisk fragment with a unique name. Abort if none or multiple fragments are found.
-[[nodiscard]] Result<const vendor_ramdisk_table_entry_v4*> find_unique_ramdisk(
- const std::string& ramdisk_name, const vendor_ramdisk_table_entry_v4* table,
- uint32_t size) {
- const vendor_ramdisk_table_entry_v4* ret = nullptr;
- uint32_t idx = 0;
- const vendor_ramdisk_table_entry_v4* entry = table;
- for (; idx < size; idx++, entry++) {
- auto entry_name_c_str = reinterpret_cast<const char*>(entry->ramdisk_name);
- auto entry_name_len = strnlen(entry_name_c_str, VENDOR_RAMDISK_NAME_SIZE);
- std::string_view entry_name(entry_name_c_str, entry_name_len);
- if (entry_name == ramdisk_name) {
- if (ret != nullptr) {
- return Errorf("Multiple vendor ramdisk '{}' found, name should be unique",
- ramdisk_name.c_str());
- }
- ret = entry;
- }
- }
- if (ret == nullptr) {
- return Errorf("Vendor ramdisk '{}' not found", ramdisk_name.c_str());
- }
- return ret;
-}
-
-// Find the vendor ramdisk fragment with |ramdisk_name| within the content of |vendor_boot|, and
-// replace it with the content of |new_ramdisk|.
-[[nodiscard]] Result<std::string> replace_vendor_ramdisk_fragment(const std::string& ramdisk_name,
- const std::string& vendor_boot,
- const std::string& new_ramdisk) {
- if (auto res = check_vendor_boot_hdr(vendor_boot, 4); !res.ok()) return res.error();
- auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v4*>(vendor_boot.data());
- auto hdr_size = get_vendor_boot_header_size(hdr);
- if (!hdr_size.ok()) return hdr_size.error();
- // Refer to bootimg.h for details. Numbers are in bytes.
- const uint32_t o = round_up(*hdr_size, hdr->page_size);
- const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
- const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);
- const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
- const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);
-
- if (hdr->vendor_ramdisk_table_entry_num == std::numeric_limits<uint32_t>::max()) {
- return Errorf("Too many vendor ramdisk entries in table, overflow");
- }
-
- // Find entry with name |ramdisk_name|.
- auto old_table_start =
- reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(vendor_boot.data() + o + p + q);
- auto find_res =
- find_unique_ramdisk(ramdisk_name, old_table_start, hdr->vendor_ramdisk_table_entry_num);
- if (!find_res.ok()) return find_res.error();
- const vendor_ramdisk_table_entry_v4* replace_entry = *find_res;
- uint32_t replace_idx = replace_entry - old_table_start;
-
- // Now reconstruct.
- DataUpdater updater(vendor_boot);
-
- // Copy header (O bytes), then update fields in header.
- if (auto res = updater.Copy(o); !res.ok()) return res.error();
- auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v4*>(updater.new_begin());
-
- // Copy ramdisk fragments, replace for the matching index.
- {
- auto old_ramdisk_entry = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(
- vendor_boot.data() + o + p + q);
- uint32_t new_total_ramdisk_size = 0;
- for (uint32_t new_ramdisk_idx = 0; new_ramdisk_idx < hdr->vendor_ramdisk_table_entry_num;
- new_ramdisk_idx++, old_ramdisk_entry++) {
- if (new_ramdisk_idx == replace_idx) {
- if (auto res = updater.Replace(replace_entry->ramdisk_size, new_ramdisk); !res.ok())
- return res.error();
- new_total_ramdisk_size += new_ramdisk.size();
- } else {
- if (auto res = updater.Copy(old_ramdisk_entry->ramdisk_size); !res.ok())
- return res.error();
- new_total_ramdisk_size += old_ramdisk_entry->ramdisk_size;
- }
- }
- new_hdr->vendor_ramdisk_size = new_total_ramdisk_size;
- }
-
- // Pad ramdisk to page boundary.
- const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);
- if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);
- !res.ok())
- return res.error();
- if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();
-
- // Copy DTB (Q bytes).
- if (auto res = updater.Copy(q); !res.ok()) return res.error();
-
- // Copy table, but with corresponding entries modified, including:
- // - ramdisk_size of the entry replaced
- // - ramdisk_offset of subsequent entries.
- for (uint32_t new_total_ramdisk_size = 0, new_entry_idx = 0;
- new_entry_idx < hdr->vendor_ramdisk_table_entry_num; new_entry_idx++) {
- auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());
- if (auto res = updater.Copy(hdr->vendor_ramdisk_table_entry_size); !res.ok())
- return res.error();
- new_entry->ramdisk_offset = new_total_ramdisk_size;
-
- if (new_entry_idx == replace_idx) {
- new_entry->ramdisk_size = new_ramdisk.size();
- }
- new_total_ramdisk_size += new_entry->ramdisk_size;
- }
-
- // Copy padding of R pages; this is okay because table size is not changed.
- if (auto res = updater.Copy(r - hdr->vendor_ramdisk_table_entry_num *
- hdr->vendor_ramdisk_table_entry_size);
- !res.ok())
- return res.error();
- if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + q + r); !res.ok())
- return res.error();
-
- // Copy bootconfig (S bytes).
- if (auto res = updater.Copy(s); !res.ok()) return res.error();
-
- if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();
- return updater.Finish();
-}
-
-} // namespace
-
-[[nodiscard]] Result<void> replace_vendor_ramdisk(android::base::borrowed_fd vendor_boot_fd,
- uint64_t vendor_boot_size,
- const std::string& ramdisk_name,
- android::base::borrowed_fd new_ramdisk_fd,
- uint64_t new_ramdisk_size) {
- if (new_ramdisk_size > std::numeric_limits<uint32_t>::max()) {
- return Errorf("New vendor ramdisk is too big");
- }
-
- auto vendor_boot = load_file(vendor_boot_fd, vendor_boot_size, "vendor boot");
- if (!vendor_boot.ok()) return vendor_boot.error();
- auto new_ramdisk = load_file(new_ramdisk_fd, new_ramdisk_size, "new vendor ramdisk");
- if (!new_ramdisk.ok()) return new_ramdisk.error();
-
- Result<std::string> new_vendor_boot;
- if (ramdisk_name == "default") {
- new_vendor_boot = replace_default_vendor_ramdisk(*vendor_boot, *new_ramdisk);
- } else {
- new_vendor_boot = replace_vendor_ramdisk_fragment(ramdisk_name, *vendor_boot, *new_ramdisk);
- }
- if (!new_vendor_boot.ok()) return new_vendor_boot.error();
- if (auto res = store_file(vendor_boot_fd, *new_vendor_boot, "new vendor boot image"); !res.ok())
- return res.error();
-
- return {};
-}
diff --git a/fastboot/vendor_boot_img_utils.h b/fastboot/vendor_boot_img_utils.h
deleted file mode 100644
index 0b702bc4d..000000000
--- a/fastboot/vendor_boot_img_utils.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <inttypes.h>
-
-#include <string>
-
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-
-// Replace the vendor ramdisk named |ramdisk_name| within the vendor boot image,
-// specified by |vendor_boot_fd|, with the ramdisk specified by |new_ramdisk_fd|. Checks
-// that the size of the files are |vendor_boot_size| and |new_ramdisk_size|, respectively.
-// If |ramdisk_name| is "default", replace the vendor ramdisk as a whole. Otherwise, replace
-// a vendor ramdisk fragment with the given unique name.
-[[nodiscard]] android::base::Result<void> replace_vendor_ramdisk(
- android::base::borrowed_fd vendor_boot_fd, uint64_t vendor_boot_size,
- const std::string& ramdisk_name, android::base::borrowed_fd new_ramdisk_fd,
- uint64_t new_ramdisk_size);
diff --git a/fastboot/vendor_boot_img_utils_test.cpp b/fastboot/vendor_boot_img_utils_test.cpp
deleted file mode 100644
index 1563b89c7..000000000
--- a/fastboot/vendor_boot_img_utils_test.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <filesystem>
-#include <optional>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/result.h>
-#include <android-base/strings.h>
-#include <bootimg.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <libavb/libavb.h>
-
-#include "vendor_boot_img_utils.h"
-
-using android::base::borrowed_fd;
-using android::base::ErrnoError;
-using android::base::GetExecutableDirectory;
-using android::base::ReadFdToString;
-using android::base::Result;
-using testing::AllOf;
-using testing::Each;
-using testing::Eq;
-using testing::HasSubstr;
-using testing::Not;
-using testing::Property;
-using std::string_literals::operator""s;
-
-// Expect that the Result<T> returned by |expr| is successful, and value matches |result_matcher|.
-#define EXPECT_RESULT(expr, result_matcher) \
- EXPECT_THAT(expr, AllOf(Property(&decltype(expr)::ok, Eq(true)), \
- Property(&decltype(expr)::value, result_matcher)))
-
-// Expect that the Result<T> returned by |expr| fails, and error message matches |error_matcher|.
-#define EXPECT_ERROR(expr, error_matcher) \
- do { \
- EXPECT_THAT( \
- expr, \
- AllOf(Property(&decltype(expr)::ok, Eq(false)), \
- Property(&decltype(expr)::error, \
- Property(&decltype(expr)::error_type::message, error_matcher)))); \
- } while (0)
-
-namespace {
-
-// Wrapper of fstat.
-Result<uint64_t> FileSize(borrowed_fd fd, std::filesystem::path path) {
- struct stat sb;
- if (fstat(fd.get(), &sb) == -1) return ErrnoError() << "fstat(" << path << ")";
- return sb.st_size;
-}
-
-// Seek to beginning then read the whole file.
-Result<std::string> ReadStartOfFdToString(borrowed_fd fd, std::filesystem::path path) {
- if (lseek64(fd.get(), 0, SEEK_SET) != 0)
- return ErrnoError() << "lseek64(" << path << ", 0, SEEK_SET)";
- std::string content;
- if (!android::base::ReadFdToString(fd, &content)) return ErrnoError() << "read(" << path << ")";
- return content;
-}
-
-// Round |value| up to page boundary.
-inline uint32_t round_up(uint32_t value, uint32_t page_size) {
- return (value + page_size - 1) / page_size * page_size;
-}
-
-// Match is successful if |arg| is a zero-padded version of |expected|.
-MATCHER_P(IsPadded, expected, (negation ? "is" : "isn't") + " zero-padded of expected value"s) {
- if (arg.size() < expected.size()) return false;
- if (0 != memcmp(arg.data(), expected.data(), expected.size())) return false;
- auto remainder = std::string_view(arg).substr(expected.size());
- for (char e : remainder)
- if (e != '\0') return false;
- return true;
-}
-
-// Same as Eq, but don't print the content to avoid spam.
-MATCHER_P(MemEq, expected, (negation ? "is" : "isn't") + " expected value"s) {
- if (arg.size() != expected.size()) return false;
- return 0 == memcmp(arg.data(), expected.data(), expected.size());
-}
-
-// Expect that |arg| and |expected| has the same AVB footer.
-MATCHER_P(HasSameAvbFooter, expected,
- (negation ? "has" : "does not have") + "expected AVB footer"s) {
- if (expected.size() < AVB_FOOTER_SIZE || arg.size() < AVB_FOOTER_SIZE) return false;
- return std::string_view(expected).substr(expected.size() - AVB_FOOTER_SIZE) ==
- std::string_view(arg).substr(arg.size() - AVB_FOOTER_SIZE);
-}
-
-// A lazy handle of a file.
-struct TestFileHandle {
- virtual ~TestFileHandle() = default;
- // Lazily call OpenImpl(), cache result in open_result_.
- android::base::Result<void> Open() {
- if (!open_result_.has_value()) open_result_ = OpenImpl();
- return open_result_.value();
- }
- // The original size at the time when the file is opened. If the file has been modified,
- // this field is NOT updated.
- uint64_t size() {
- CHECK(open_result_.has_value());
- return size_;
- }
- // The current size of the file. If the file has been modified since opened,
- // this is updated.
- Result<uint64_t> fsize() {
- CHECK(open_result_.has_value());
- return FileSize(fd_, abs_path_);
- }
- borrowed_fd fd() {
- CHECK(open_result_.has_value());
- return fd_;
- }
- Result<std::string> Read() {
- CHECK(open_result_.has_value());
- return ReadStartOfFdToString(fd_, abs_path_);
- }
-
- private:
- std::filesystem::path abs_path_;
- uint64_t size_;
- std::optional<android::base::Result<void>> open_result_;
- borrowed_fd fd_{-1};
- // Opens |rel_path_| as a readonly fd, pass it to Transform, and store result to
- // |borrowed_fd_|.
- android::base::Result<void> OpenImpl() {
- android::base::unique_fd read_fd(TEMP_FAILURE_RETRY(
- open(abs_path_.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY)));
- if (!read_fd.ok()) return ErrnoError() << "open(" << abs_path_ << ")";
- auto size = FileSize(read_fd, abs_path_);
- if (!size.ok()) return size.error();
- size_ = *size;
-
- auto borrowed_fd = Transform(abs_path_, std::move(read_fd));
- if (!borrowed_fd.ok()) return borrowed_fd.error();
- fd_ = borrowed_fd.value();
-
- return {};
- }
-
- protected:
- // |rel_path| is the relative path under test data directory.
- TestFileHandle(const std::filesystem::path& rel_path)
- : abs_path_(std::move(std::filesystem::path(GetExecutableDirectory()) / rel_path)) {}
- // Given |read_fd|, the readonly fd on the test file, return an fd that's suitable for client
- // to use. Implementation is responsible for managing the lifetime of the returned fd.
- virtual android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,
- android::base::unique_fd read_fd) = 0;
-};
-
-// A TestFileHandle where the file is readonly.
-struct ReadOnlyTestFileHandle : TestFileHandle {
- ReadOnlyTestFileHandle(const std::filesystem::path& rel_path) : TestFileHandle(rel_path) {}
-
- private:
- android::base::unique_fd owned_fd_;
- android::base::Result<borrowed_fd> Transform(const std::filesystem::path&,
- android::base::unique_fd read_fd) override {
- owned_fd_ = std::move(read_fd);
- return owned_fd_;
- }
-};
-
-// A TestFileHandle where the test file is copies, hence read-writable.
-struct ReadWriteTestFileHandle : TestFileHandle {
- ReadWriteTestFileHandle(const std::filesystem::path& rel_path) : TestFileHandle(rel_path) {}
-
- private:
- std::unique_ptr<TemporaryFile> temp_file_;
-
- android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,
- android::base::unique_fd read_fd) override {
- // Make a copy to avoid writing to test data. Test files are small, so it is okay
- // to read the whole file.
- auto content = ReadStartOfFdToString(read_fd, abs_path);
- if (!content.ok()) return content.error();
- temp_file_ = std::make_unique<TemporaryFile>();
- if (temp_file_->fd == -1)
- return ErrnoError() << "copy " << abs_path << ": open temp file failed";
- if (!android::base::WriteStringToFd(*content, temp_file_->fd))
- return ErrnoError() << "copy " << abs_path << ": write temp file failed";
-
- return temp_file_->fd;
- }
-};
-
-class RepackVendorBootImgTestEnv : public ::testing::Environment {
- public:
- virtual void SetUp() {
- OpenTestFile("test_dtb.img", &dtb, &dtb_content);
- OpenTestFile("test_bootconfig.img", &bootconfig, &bootconfig_content);
- OpenTestFile("test_vendor_ramdisk_none.img", &none, &none_content);
- OpenTestFile("test_vendor_ramdisk_platform.img", &platform, &platform_content);
- OpenTestFile("test_vendor_ramdisk_replace.img", &replace, &replace_content);
- }
-
- std::unique_ptr<TestFileHandle> dtb;
- std::string dtb_content;
- std::unique_ptr<TestFileHandle> bootconfig;
- std::string bootconfig_content;
- std::unique_ptr<TestFileHandle> none;
- std::string none_content;
- std::unique_ptr<TestFileHandle> platform;
- std::string platform_content;
- std::unique_ptr<TestFileHandle> replace;
- std::string replace_content;
-
- private:
- void OpenTestFile(const char* rel_path, std::unique_ptr<TestFileHandle>* handle,
- std::string* content) {
- *handle = std::make_unique<ReadOnlyTestFileHandle>(rel_path);
- ASSERT_RESULT_OK((*handle)->Open());
- auto content_res = (*handle)->Read();
- ASSERT_RESULT_OK(content_res);
- *content = *content_res;
- }
-};
-RepackVendorBootImgTestEnv* env = nullptr;
-
-struct RepackVendorBootImgTestParam {
- std::string vendor_boot_file_name;
- uint32_t expected_header_version;
- friend std::ostream& operator<<(std::ostream& os, const RepackVendorBootImgTestParam& param) {
- return os << param.vendor_boot_file_name;
- }
-};
-
-class RepackVendorBootImgTest : public ::testing::TestWithParam<RepackVendorBootImgTestParam> {
- public:
- virtual void SetUp() {
- vboot = std::make_unique<ReadWriteTestFileHandle>(GetParam().vendor_boot_file_name);
- ASSERT_RESULT_OK(vboot->Open());
- }
- std::unique_ptr<TestFileHandle> vboot;
-};
-
-TEST_P(RepackVendorBootImgTest, InvalidSize) {
- EXPECT_ERROR(replace_vendor_ramdisk(vboot->fd(), vboot->size() + 1, "default",
- env->replace->fd(), env->replace->size()),
- HasSubstr("Size of vendor boot does not match"));
- EXPECT_ERROR(replace_vendor_ramdisk(vboot->fd(), vboot->size(), "default", env->replace->fd(),
- env->replace->size() + 1),
- HasSubstr("Size of new vendor ramdisk does not match"));
-}
-
-TEST_P(RepackVendorBootImgTest, ReplaceUnknown) {
- auto res = replace_vendor_ramdisk(vboot->fd(), vboot->size(), "unknown", env->replace->fd(),
- env->replace->size());
- if (GetParam().expected_header_version == 3) {
- EXPECT_ERROR(res, Eq("Require vendor boot header V4 but is V3"));
- } else if (GetParam().expected_header_version == 4) {
- EXPECT_ERROR(res, Eq("Vendor ramdisk 'unknown' not found"));
- }
-}
-
-TEST_P(RepackVendorBootImgTest, ReplaceDefault) {
- auto old_content = vboot->Read();
- ASSERT_RESULT_OK(old_content);
-
- ASSERT_RESULT_OK(replace_vendor_ramdisk(vboot->fd(), vboot->size(), "default",
- env->replace->fd(), env->replace->size()));
- EXPECT_RESULT(vboot->fsize(), vboot->size()) << "File size should not change after repack";
-
- auto new_content_res = vboot->Read();
- ASSERT_RESULT_OK(new_content_res);
- std::string_view new_content(*new_content_res);
-
- auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(new_content.data());
- ASSERT_EQ(0, memcmp(VENDOR_BOOT_MAGIC, hdr->magic, VENDOR_BOOT_MAGIC_SIZE));
- ASSERT_EQ(GetParam().expected_header_version, hdr->header_version);
- EXPECT_EQ(hdr->vendor_ramdisk_size, env->replace->size());
- EXPECT_EQ(hdr->dtb_size, env->dtb->size());
-
- auto o = round_up(sizeof(vendor_boot_img_hdr_v3), hdr->page_size);
- auto p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
- auto q = round_up(hdr->dtb_size, hdr->page_size);
-
- EXPECT_THAT(new_content.substr(o, p), IsPadded(env->replace_content));
- EXPECT_THAT(new_content.substr(o + p, q), IsPadded(env->dtb_content));
-
- if (hdr->header_version < 4) return;
-
- auto hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(hdr);
- EXPECT_EQ(hdr_v4->vendor_ramdisk_table_entry_num, 1);
- EXPECT_EQ(hdr_v4->vendor_ramdisk_table_size, 1 * hdr_v4->vendor_ramdisk_table_entry_size);
- EXPECT_GE(hdr_v4->vendor_ramdisk_table_entry_size, sizeof(vendor_ramdisk_table_entry_v4));
- auto entry = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(&new_content[o + p + q]);
- EXPECT_EQ(entry->ramdisk_offset, 0);
- EXPECT_EQ(entry->ramdisk_size, hdr_v4->vendor_ramdisk_size);
- EXPECT_EQ(entry->ramdisk_type, VENDOR_RAMDISK_TYPE_NONE);
-
- EXPECT_EQ(hdr_v4->bootconfig_size, env->bootconfig->size());
- auto r = round_up(hdr_v4->vendor_ramdisk_table_size, hdr_v4->page_size);
- auto s = round_up(hdr_v4->bootconfig_size, hdr_v4->page_size);
- EXPECT_THAT(new_content.substr(o + p + q + r, s), IsPadded(env->bootconfig_content));
-
- EXPECT_THAT(new_content, HasSameAvbFooter(*old_content));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- RepackVendorBootImgTest, RepackVendorBootImgTest,
- ::testing::Values(RepackVendorBootImgTestParam{"vendor_boot_v3.img", 3},
- RepackVendorBootImgTestParam{"vendor_boot_v4_with_frag.img", 4},
- RepackVendorBootImgTestParam{"vendor_boot_v4_without_frag.img", 4}),
- [](const auto& info) {
- return android::base::StringReplace(info.param.vendor_boot_file_name, ".", "_", false);
- });
-
-std::string_view GetRamdiskName(const vendor_ramdisk_table_entry_v4* entry) {
- auto ramdisk_name = reinterpret_cast<const char*>(entry->ramdisk_name);
- return std::string_view(ramdisk_name, strnlen(ramdisk_name, VENDOR_RAMDISK_NAME_SIZE));
-}
-
-class RepackVendorBootImgTestV4 : public ::testing::TestWithParam<uint32_t /* ramdisk type */> {
- public:
- virtual void SetUp() {
- vboot = std::make_unique<ReadWriteTestFileHandle>("vendor_boot_v4_with_frag.img");
- ASSERT_RESULT_OK(vboot->Open());
- }
- std::unique_ptr<TestFileHandle> vboot;
-};
-
-TEST_P(RepackVendorBootImgTestV4, Replace) {
- uint32_t replace_ramdisk_type = GetParam();
- std::string replace_ramdisk_name;
- std::string expect_new_ramdisk_content;
- uint32_t expect_none_size = env->none->size();
- uint32_t expect_platform_size = env->platform->size();
- switch (replace_ramdisk_type) {
- case VENDOR_RAMDISK_TYPE_NONE:
- replace_ramdisk_name = "none_ramdisk";
- expect_new_ramdisk_content = env->replace_content + env->platform_content;
- expect_none_size = env->replace->size();
- break;
- case VENDOR_RAMDISK_TYPE_PLATFORM:
- replace_ramdisk_name = "platform_ramdisk";
- expect_new_ramdisk_content = env->none_content + env->replace_content;
- expect_platform_size = env->replace->size();
- break;
- default:
- LOG(FATAL) << "Ramdisk type " << replace_ramdisk_type
- << " is not supported by this test.";
- }
-
- auto old_content = vboot->Read();
- ASSERT_RESULT_OK(old_content);
-
- ASSERT_RESULT_OK(replace_vendor_ramdisk(vboot->fd(), vboot->size(), replace_ramdisk_name,
- env->replace->fd(), env->replace->size()));
- EXPECT_RESULT(vboot->fsize(), vboot->size()) << "File size should not change after repack";
-
- auto new_content_res = vboot->Read();
- ASSERT_RESULT_OK(new_content_res);
- std::string_view new_content(*new_content_res);
-
- auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v4*>(new_content.data());
- ASSERT_EQ(0, memcmp(VENDOR_BOOT_MAGIC, hdr->magic, VENDOR_BOOT_MAGIC_SIZE));
- ASSERT_EQ(4, hdr->header_version);
- EXPECT_EQ(hdr->vendor_ramdisk_size, expect_none_size + expect_platform_size);
- EXPECT_EQ(hdr->dtb_size, env->dtb->size());
- EXPECT_EQ(hdr->bootconfig_size, env->bootconfig->size());
-
- auto o = round_up(sizeof(vendor_boot_img_hdr_v3), hdr->page_size);
- auto p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
- auto q = round_up(hdr->dtb_size, hdr->page_size);
- auto r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
- auto s = round_up(hdr->bootconfig_size, hdr->page_size);
-
- EXPECT_THAT(new_content.substr(o, p), IsPadded(expect_new_ramdisk_content));
- EXPECT_THAT(new_content.substr(o + p, q), IsPadded(env->dtb_content));
-
- // Check changes in table.
- EXPECT_EQ(hdr->vendor_ramdisk_table_entry_num, 2);
- EXPECT_EQ(hdr->vendor_ramdisk_table_size, 2 * hdr->vendor_ramdisk_table_entry_size);
- EXPECT_GE(hdr->vendor_ramdisk_table_entry_size, sizeof(vendor_ramdisk_table_entry_v4));
- auto entry_none =
- reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(&new_content[o + p + q]);
- EXPECT_EQ(entry_none->ramdisk_offset, 0);
- EXPECT_EQ(entry_none->ramdisk_size, expect_none_size);
- EXPECT_EQ(entry_none->ramdisk_type, VENDOR_RAMDISK_TYPE_NONE);
- EXPECT_EQ(GetRamdiskName(entry_none), "none_ramdisk");
-
- auto entry_platform = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(
- &new_content[o + p + q + hdr->vendor_ramdisk_table_entry_size]);
- EXPECT_EQ(entry_platform->ramdisk_offset, expect_none_size);
- EXPECT_EQ(entry_platform->ramdisk_size, expect_platform_size);
- EXPECT_EQ(entry_platform->ramdisk_type, VENDOR_RAMDISK_TYPE_PLATFORM);
- EXPECT_EQ(GetRamdiskName(entry_platform), "platform_ramdisk");
-
- EXPECT_THAT(new_content.substr(o + p + q + r, s), IsPadded(env->bootconfig_content));
-
- EXPECT_THAT(new_content, HasSameAvbFooter(*old_content));
-}
-INSTANTIATE_TEST_SUITE_P(RepackVendorBootImgTest, RepackVendorBootImgTestV4,
- ::testing::Values(VENDOR_RAMDISK_TYPE_NONE, VENDOR_RAMDISK_TYPE_PLATFORM),
- [](const auto& info) {
- return info.param == VENDOR_RAMDISK_TYPE_NONE ? "none" : "platform";
- });
-
-} // namespace
-
-int main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
- env = static_cast<RepackVendorBootImgTestEnv*>(
- testing::AddGlobalTestEnvironment(new RepackVendorBootImgTestEnv));
- return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3c83aabba..ac784b249 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -14,34 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["system_core_fs_mgr_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "system_core_fs_mgr_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-MIT",
- ],
- // large-scale-change unable to identify any license_text files
-}
-
cc_defaults {
name: "fs_mgr_defaults",
sanitize: {
@@ -63,7 +35,6 @@ cc_defaults {
"-D_FILE_OFFSET_BITS=64",
],
srcs: [
- "blockdev.cpp",
"file_wait.cpp",
"fs_mgr.cpp",
"fs_mgr_format.cpp",
@@ -199,7 +170,6 @@ cc_binary {
defaults: ["fs_mgr_defaults"],
static_libs: [
"libavb_user",
- "libgsid",
"libutils",
"libvold_binder",
],
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index cf353a1ed..cbbd3bc05 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,3 @@
bowgotsai@google.com
dvander@google.com
+tomcherry@google.com
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index 94b2f8c0b..f57907862 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -8,8 +8,8 @@ Users working with userdebug or eng builds expect to be able to remount the
system partition as read-write and then add or modify any number of files
without reflashing the system image, which is efficient for a development cycle.
-Limited memory systems use read-only types of file systems or dynamic
-Android partitions (DAPs). These file systems land system partition images
+Limited memory systems use read-only types of file systems or logical resizable
+Android partitions (LRAPs). These file systems land system partition images
right-sized, and have been deduped at the block level to compress the content.
This means that a remount either isn’t possible, or isn't useful because of
space limitations or support logistics.
@@ -42,7 +42,7 @@ Then enter one of the following sequences:
$ adb push <source> <destination>
$ adb reboot
-Note that you can replace these two lines in the above sequence:
+Note that you can replace these two lines:
$ adb disable-verity
$ adb reboot
@@ -51,7 +51,7 @@ with this line:
$ adb remount -R
-**Note:** _adb remount -R_ won’t reboot if the device is already in the adb remount state.
+**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
None of this changes if OverlayFS needs to be engaged.
The decisions whether to use traditional direct file-system remount,
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 84709b6a8..676f446e7 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -7,19 +7,13 @@
"name": "liblp_test"
},
{
- "name": "fiemap_image_test"
+ "name": "fiemap_image_test_presubmit"
},
{
"name": "fiemap_writer_test"
},
{
"name": "vts_libsnapshot_test"
- },
- {
- "name": "libsnapshot_fuzzer_test"
- },
- {
- "name": "cow_api_test"
}
]
}
diff --git a/fs_mgr/blockdev.cpp b/fs_mgr/blockdev.cpp
deleted file mode 100644
index 14b217ca8..000000000
--- a/fs_mgr/blockdev.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include <dirent.h>
-#include <libdm/dm.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include "blockdev.h"
-
-using android::base::Basename;
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::base::ResultError;
-using android::base::StartsWith;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-
-// Return the parent device of a partition. Converts e.g. "sda26" into "sda".
-static std::string PartitionParent(const std::string& blockdev) {
- if (blockdev.find('/') != std::string::npos) {
- LOG(ERROR) << __func__ << ": invalid argument " << blockdev;
- return blockdev;
- }
- auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir};
- if (!dir) {
- return blockdev;
- }
- for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
- if (ent->d_name[0] == '.') {
- continue;
- }
- std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str());
- struct stat statbuf;
- if (stat(path.c_str(), &statbuf) >= 0) {
- return ent->d_name;
- }
- }
- return blockdev;
-}
-
-// Convert a major:minor pair into a block device name.
-static std::string BlockdevName(dev_t dev) {
- auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
- if (!dir) {
- return {};
- }
- for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
- if (ent->d_name[0] == '.') {
- continue;
- }
- const std::string path = std::string("/dev/block/") + ent->d_name;
- struct stat statbuf;
- if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {
- return ent->d_name;
- }
- }
- return {};
-}
-
-// Trim whitespace from the end of a string.
-static void rtrim(std::string& s) {
- s.erase(s.find_last_not_of('\n') + 1, s.length());
-}
-
-// For file `file_path`, retrieve the block device backing the filesystem on
-// which the file exists and return the queue depth of the block device.
-static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
- struct stat statbuf;
- int res = stat(file_path.c_str(), &statbuf);
- if (res < 0) {
- return ErrnoError() << "stat(" << file_path << ")";
- }
- std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
- LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
- if (blockdev.empty()) {
- const std::string err_msg =
- StringPrintf("Failed to convert %u:%u (path %s)", major(statbuf.st_dev),
- minor(statbuf.st_dev), file_path.c_str());
- return ResultError(err_msg, 0);
- }
- auto& dm = DeviceMapper::Instance();
- for (;;) {
- std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
- if (!child) {
- break;
- }
- LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child;
- blockdev = *child;
- }
- std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
- if (!maybe_blockdev) {
- return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
- }
- blockdev = PartitionParent(*maybe_blockdev);
- LOG(DEBUG) << __func__ << ": "
- << "Partition parent: " << blockdev;
- const std::string nr_tags_path =
- StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
- std::string nr_tags;
- if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
- return ResultError("Failed to read " + nr_tags_path, 0);
- }
- rtrim(nr_tags);
- LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
- << " and that block device supports queue depth " << nr_tags;
- return strtol(nr_tags.c_str(), NULL, 0);
-}
-
-// Set 'nr_requests' of `loop_device_path` to the queue depth of the block
-// device backing `file_path`.
-Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
- const std::string& file_path) {
- if (!StartsWith(loop_device_path, "/dev/")) {
- return Error() << "Invalid argument " << loop_device_path;
- }
-
- const std::string loop_device_name = Basename(loop_device_path);
-
- const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
- if (!qd.ok()) {
- LOG(DEBUG) << __func__ << ": "
- << "BlockDeviceQueueDepth() returned " << qd.error();
- return ResultError(qd.error());
- }
- const std::string nr_requests = StringPrintf("%u", *qd);
- const std::string sysfs_path =
- StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str());
- unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
- if (sysfs_fd == -1) {
- return ErrnoError() << "Failed to open " << sysfs_path;
- }
-
- const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());
- if (res < 0) {
- return ErrnoError() << "Failed to write to " << sysfs_path;
- }
- return {};
-}
diff --git a/fs_mgr/blockdev.h b/fs_mgr/blockdev.h
deleted file mode 100644
index 2c0d68a77..000000000
--- a/fs_mgr/blockdev.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/result.h>
-#include <string>
-
-android::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
- const std::string& file_path);
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 21df8af8c..956147198 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -70,7 +70,6 @@
#include <log/log_properties.h>
#include <logwrap/logwrap.h>
-#include "blockdev.h"
#include "fs_mgr_priv.h"
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
@@ -265,12 +264,12 @@ static void check_fs(const std::string& blk_device, const std::string& fs_type,
F2FS_FSCK_BIN, "-f", "-c", "10000", "--debug-cache", blk_device.c_str()};
if (should_force_check(*fs_stat)) {
- LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
+ LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache"
<< realpath(blk_device);
ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
&status, false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
} else {
- LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
+ LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache"
<< realpath(blk_device);
ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
@@ -302,10 +301,13 @@ static bool is_ext4_superblock_valid(const struct ext4_super_block* es) {
return true;
}
+static bool needs_block_encryption(const FstabEntry& entry);
+static bool should_use_metadata_encryption(const FstabEntry& entry);
+
// Read the primary superblock from an ext4 filesystem. On failure return
// false. If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.
-static bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,
- int* fs_stat) {
+static bool read_ext4_superblock(const std::string& blk_device, const FstabEntry& entry,
+ struct ext4_super_block* sb, int* fs_stat) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
@@ -322,7 +324,29 @@ static bool read_ext4_superblock(const std::string& blk_device, struct ext4_supe
LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
// not a valid fs, tune2fs, fsck, and mount will all fail.
*fs_stat |= FS_STAT_INVALID_MAGIC;
- return false;
+
+ bool encrypted = should_use_metadata_encryption(entry) || needs_block_encryption(entry);
+ if (entry.mount_point == "/data" &&
+ (!encrypted || android::base::StartsWith(blk_device, "/dev/block/dm-"))) {
+ // try backup superblock, if main superblock is corrupted
+ for (unsigned int blocksize = EXT4_MIN_BLOCK_SIZE; blocksize <= EXT4_MAX_BLOCK_SIZE;
+ blocksize *= 2) {
+ uint64_t superblock = blocksize * 8;
+ if (blocksize == EXT4_MIN_BLOCK_SIZE) superblock++;
+
+ if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), superblock * blocksize)) !=
+ sizeof(*sb)) {
+ PERROR << "Can't read '" << blk_device << "' superblock";
+ return false;
+ }
+ if (is_ext4_superblock_valid(sb) &&
+ (1 << (10 + sb->s_log_block_size) == blocksize)) {
+ *fs_stat &= ~FS_STAT_INVALID_MAGIC;
+ break;
+ }
+ }
+ }
+ if (*fs_stat & FS_STAT_INVALID_MAGIC) return false;
}
*fs_stat |= FS_STAT_IS_EXT4;
LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
@@ -522,13 +546,13 @@ static void tune_verity(const std::string& blk_device, const FstabEntry& entry,
}
// Enable casefold if needed.
-static void tune_casefold(const std::string& blk_device, const FstabEntry& entry,
- const struct ext4_super_block* sb, int* fs_stat) {
+static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
+ int* fs_stat) {
bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
bool wants_casefold =
android::base::GetBoolProperty("external_storage.casefold.enabled", false);
- if (entry.mount_point != "/data" || !wants_casefold || has_casefold) return;
+ if (!wants_casefold || has_casefold) return;
std::string casefold_support;
if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {
@@ -583,10 +607,11 @@ static void tune_metadata_csum(const std::string& blk_device, const FstabEntry&
LINFO << "Enabling ext4 metadata_csum on " << blk_device;
- // Must give `-T now` to prevent last_fsck_time from growing too large,
+ // requires to give last_fsck_time to current to avoid insane time.
// otherwise, tune2fs won't enable metadata_csum.
+ std::string now = std::to_string(time(0));
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
- "-T", "now", blk_device.c_str()};
+ "-T", now.c_str(), blk_device.c_str()};
const char* resize2fs_args[] = {RESIZE2FS_BIN, "-b", blk_device.c_str()};
if (!run_command(tune2fs_args, ARRAY_SIZE(tune2fs_args))) {
@@ -648,46 +673,6 @@ bool fs_mgr_is_f2fs(const std::string& blk_device) {
return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
}
-static void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {
- std::string block_device;
- if (!Realpath(entry_block_device, &block_device)) {
- PERROR << "Failed to realpath " << entry_block_device;
- return;
- }
-
- static constexpr std::string_view kDevBlockPrefix("/dev/block/");
- if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {
- LWARNING << block_device << " is not a block device";
- return;
- }
-
- DeviceMapper& dm = DeviceMapper::Instance();
- while (true) {
- std::string block_name = block_device;
- if (android::base::StartsWith(block_device, kDevBlockPrefix)) {
- block_name = block_device.substr(kDevBlockPrefix.length());
- }
- std::string sys_partition =
- android::base::StringPrintf("/sys/class/block/%s/partition", block_name.c_str());
- struct stat info;
- if (lstat(sys_partition.c_str(), &info) == 0) {
- // it has a partition like "sda12".
- block_name += "/..";
- }
- std::string sys_ra = android::base::StringPrintf("/sys/class/block/%s/queue/read_ahead_kb",
- block_name.c_str());
- std::string size = android::base::StringPrintf("%llu", (long long)size_kb);
- android::base::WriteStringToFile(size, sys_ra.c_str());
- LINFO << "Set readahead_kb: " << size << " on " << sys_ra;
-
- auto parent = dm.GetParentBlockDeviceByPath(block_device);
- if (!parent) {
- return;
- }
- block_device = *parent;
- }
-}
-
//
// Prepare the filesystem on the given block device to be mounted.
//
@@ -697,28 +682,13 @@ static void SetReadAheadSize(const std::string& entry_block_device, off64_t size
// If needed, we'll also enable (or disable) filesystem features as specified by
// the fstab record.
//
-static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry,
- const std::string& alt_mount_point = "") {
- auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
- // We need this because sometimes we have legacy symlinks that are
- // lingering around and need cleaning up.
- struct stat info;
- if (lstat(mount_point.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
- unlink(mount_point.c_str());
- }
- mkdir(mount_point.c_str(), 0755);
-
- // Don't need to return error, since it's a salt
- if (entry.readahead_size_kb != -1) {
- SetReadAheadSize(blk_device, entry.readahead_size_kb);
- }
-
+static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry) {
int fs_stat = 0;
if (is_extfs(entry.fs_type)) {
struct ext4_super_block sb;
- if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+ if (read_ext4_superblock(blk_device, entry, &sb, &fs_stat)) {
if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
(sb.s_state & EXT4_VALID_FS) == 0) {
LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
@@ -740,7 +710,7 @@ static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry&
if (entry.fs_mgr_flags.check ||
(fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
- check_fs(blk_device, entry.fs_type, mount_point, &fs_stat);
+ check_fs(blk_device, entry.fs_type, entry.mount_point, &fs_stat);
}
if (is_extfs(entry.fs_type) &&
@@ -748,11 +718,11 @@ static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry&
entry.fs_mgr_flags.fs_verity || entry.fs_mgr_flags.ext_meta_csum)) {
struct ext4_super_block sb;
- if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+ if (read_ext4_superblock(blk_device, entry, &sb, &fs_stat)) {
tune_reserved_size(blk_device, entry, &sb, &fs_stat);
tune_encrypt(blk_device, entry, &sb, &fs_stat);
tune_verity(blk_device, entry, &sb, &fs_stat);
- tune_casefold(blk_device, entry, &sb, &fs_stat);
+ tune_casefold(blk_device, &sb, &fs_stat);
tune_metadata_csum(blk_device, entry, &sb, &fs_stat);
}
}
@@ -785,6 +755,13 @@ bool fs_mgr_is_device_unlocked() {
// sets the underlying block device to read-only if the mount is read-only.
// See "man 2 mount" for return values.
static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
+ // We need this because sometimes we have legacy symlinks that are
+ // lingering around and need cleaning up.
+ struct stat info;
+ if (lstat(target.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
+ unlink(target.c_str());
+ }
+ mkdir(target.c_str(), 0755);
errno = 0;
unsigned long mountflags = entry.flags;
int ret = 0;
@@ -1045,19 +1022,6 @@ static int handle_encryptable(const FstabEntry& entry) {
}
}
-static void set_type_property(int status) {
- switch (status) {
- case FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED:
- SetProperty("ro.crypto.type", "block");
- break;
- case FS_MGR_MNTALL_DEV_FILE_ENCRYPTED:
- case FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED:
- case FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION:
- SetProperty("ro.crypto.type", "file");
- break;
- }
-}
-
static bool call_vdc(const std::vector<std::string>& args, int* ret) {
std::vector<char const*> argv;
argv.emplace_back("/system/bin/vdc");
@@ -1195,9 +1159,10 @@ class CheckpointManager {
// metadata-encrypted device with smaller blocks, we must not change this for
// devices shipped with Q or earlier unless they explicitly selected dm-default-key
// v2
+ constexpr unsigned int pre_gki_level = __ANDROID_API_Q__;
unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
"ro.crypto.dm_default_key.options_format.version",
- (android::fscrypt::GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));
+ (android::fscrypt::GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
if (options_format_version > 1) {
bowTarget->SetBlockSize(4096);
}
@@ -1365,16 +1330,14 @@ static bool IsMountPointMounted(const std::string& mount_point) {
// When multiple fstab records share the same mount_point, it will try to mount each
// one in turn, and ignore any duplicates after a first successful mount.
// Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
-MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
int error_count = 0;
CheckpointManager checkpoint_manager;
AvbUniquePtr avb_handle(nullptr);
- bool wiped = false;
- bool userdata_mounted = false;
if (fstab->empty()) {
- return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+ return FS_MGR_MNTALL_FAIL;
}
// Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),
@@ -1414,7 +1377,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
// Terrible hack to make it possible to remount /data.
- // TODO: refactor fs_mgr_mount_all and get rid of this.
+ // TODO: refact fs_mgr_mount_all and get rid of this.
if (mount_mode == MOUNT_MODE_ONLY_USERDATA && current_entry.mount_point != "/data") {
continue;
}
@@ -1450,8 +1413,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
avb_handle = AvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open AvbHandle";
- set_type_property(encryptable);
- return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+ return FS_MGR_MNTALL_FAIL;
}
}
if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==
@@ -1493,7 +1455,7 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
if (status == FS_MGR_MNTALL_FAIL) {
// Fatal error - no point continuing.
- return {status, userdata_mounted};
+ return status;
}
if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
@@ -1504,25 +1466,20 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
encryptable = status;
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
- attempted_entry.mount_point, wiped ? "true" : "false",
- attempted_entry.fs_type},
+ attempted_entry.mount_point},
nullptr)) {
LERROR << "Encryption failed";
- set_type_property(encryptable);
- return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+ return FS_MGR_MNTALL_FAIL;
}
}
}
- if (current_entry.mount_point == "/data") {
- userdata_mounted = true;
- }
// Success! Go get the next one.
continue;
}
// Mounting failed, understand why and retry.
- wiped = partition_wiped(current_entry.blk_device.c_str());
+ bool wiped = partition_wiped(current_entry.blk_device.c_str());
bool crypt_footer = false;
if (mount_errno != EBUSY && mount_errno != EACCES &&
current_entry.fs_mgr_flags.formattable && wiped) {
@@ -1547,27 +1504,6 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
} else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
crypt_footer = true;
}
-
- // EncryptInplace will be used when vdc gives an error or needs to format partitions
- // other than /data
- if (should_use_metadata_encryption(current_entry) &&
- current_entry.mount_point == "/data") {
-
- // vdc->Format requires "ro.crypto.type" to set an encryption flag
- encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
- set_type_property(encryptable);
-
- if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
- current_entry.mount_point, "true" /* shouldFormat */,
- current_entry.fs_type},
- nullptr)) {
- LERROR << "Encryption failed";
- } else {
- userdata_mounted = true;
- continue;
- }
- }
-
if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
// Let's replay the mount actions.
i = top_idx - 1;
@@ -1606,8 +1542,6 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
attempted_entry.mount_point},
nullptr)) {
++error_count;
- } else if (current_entry.mount_point == "/data") {
- userdata_mounted = true;
}
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
continue;
@@ -1632,16 +1566,14 @@ MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
}
- set_type_property(encryptable);
-
#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
fs_mgr_overlayfs_mount_all(fstab);
#endif
if (error_count) {
- return {FS_MGR_MNTALL_FAIL, userdata_mounted};
+ return FS_MGR_MNTALL_FAIL;
} else {
- return {encryptable, userdata_mounted};
+ return encryptable;
}
}
@@ -1863,26 +1795,25 @@ int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
}
LINFO << "Remounting /data";
// TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
- auto result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
- return result.code == FS_MGR_MNTALL_FAIL ? -1 : 0;
+ int result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
+ return result == FS_MGR_MNTALL_FAIL ? -1 : 0;
}
return 0;
}
// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
-int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
// First check the filesystem if requested.
if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
LERROR << "Skipping mounting '" << entry.blk_device << "'";
}
- auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
-
// Run fsck if needed
- prepare_fs_for_mount(entry.blk_device, entry, mount_point);
+ prepare_fs_for_mount(entry.blk_device, entry);
- int ret = __mount(entry.blk_device, mount_point, entry);
+ int ret =
+ __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
}
@@ -1941,14 +1872,7 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
continue;
}
- // Now mount it where requested */
- if (tmp_mount_point) {
- mount_point = tmp_mount_point;
- } else {
- mount_point = fstab_entry.mount_point;
- }
-
- int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry, mount_point);
+ int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry);
if (fstab_entry.fs_mgr_flags.avb) {
if (!avb_handle) {
@@ -1982,6 +1906,12 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
}
}
+ // Now mount it where requested */
+ if (tmp_mount_point) {
+ mount_point = tmp_mount_point;
+ } else {
+ mount_point = fstab_entry.mount_point;
+ }
int retry_count = 2;
while (retry_count-- > 0) {
if (!__mount(n_blk_device, mount_point, fstab_entry)) {
@@ -1993,7 +1923,7 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
mount_errors++;
fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
// try again after fsck
- check_fs(n_blk_device, fstab_entry.fs_type, mount_point, &fs_stat);
+ check_fs(n_blk_device, fstab_entry.fs_type, fstab_entry.mount_point, &fs_stat);
}
}
log_fs_stat(fstab_entry.blk_device, fs_stat);
@@ -2066,24 +1996,22 @@ static bool PrepareZramBackingDevice(off64_t size) {
// Allocate loop device and attach it to file_path.
LoopControl loop_control;
- std::string loop_device;
- if (!loop_control.Attach(target_fd.get(), 5s, &loop_device)) {
+ std::string device;
+ if (!loop_control.Attach(target_fd.get(), 5s, &device)) {
return false;
}
- ConfigureQueueDepth(loop_device, "/");
-
// set block size & direct IO
- unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
- if (loop_fd.get() == -1) {
- PERROR << "Cannot open " << loop_device;
+ unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+ if (device_fd.get() == -1) {
+ PERROR << "Cannot open " << device;
return false;
}
- if (!LoopControl::EnableDirectIo(loop_fd.get())) {
+ if (!LoopControl::EnableDirectIo(device_fd.get())) {
return false;
}
- return InstallZramDevice(loop_device);
+ return InstallZramDevice(device);
}
bool fs_mgr_swapon_all(const Fstab& fstab) {
@@ -2191,41 +2119,6 @@ bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
return false;
}
-std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry) {
- if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
- return "";
- }
- DeviceMapper& dm = DeviceMapper::Instance();
- std::string device = GetVerityDeviceName(entry);
-
- std::vector<DeviceMapper::TargetInfo> table;
- if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
- return "";
- }
- for (const auto& target : table) {
- if (strcmp(target.spec.target_type, "verity") != 0) {
- continue;
- }
-
- // The format is stable for dm-verity version 0 & 1. And the data is expected to have
- // the fixed format:
- // <version> <dev> <hash_dev> <data_block_size> <hash_block_size> <num_data_blocks>
- // <hash_start_block> <algorithm> <digest> <salt>
- // Details in https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html
-
- std::vector<std::string> tokens = android::base::Split(target.data, " \t\r\n");
- if (tokens[0] != "0" && tokens[0] != "1") {
- LOG(WARNING) << "Unrecognized device mapper version in " << target.data;
- return "";
- }
-
- // Hashtree algorithm is the 8th token in the output
- return android::base::Trim(tokens[7]);
- }
-
- return "";
-}
-
bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
@@ -2251,8 +2144,7 @@ std::string fs_mgr_get_super_partition_name(int slot) {
// Devices upgrading to dynamic partitions are allowed to specify a super
// partition name. This includes cuttlefish, which is a non-A/B device.
std::string super_partition;
- if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
- fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+ if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
if (fs_mgr_get_slot_suffix().empty()) {
return super_partition;
}
@@ -2268,81 +2160,3 @@ std::string fs_mgr_get_super_partition_name(int slot) {
}
return LP_METADATA_DEFAULT_PARTITION_NAME;
}
-
-bool fs_mgr_create_canonical_mount_point(const std::string& mount_point) {
- auto saved_errno = errno;
- auto ok = true;
- auto created_mount_point = !mkdir(mount_point.c_str(), 0755);
- std::string real_mount_point;
- if (!Realpath(mount_point, &real_mount_point)) {
- ok = false;
- PERROR << "failed to realpath(" << mount_point << ")";
- } else if (mount_point != real_mount_point) {
- ok = false;
- LERROR << "mount point is not canonical: realpath(" << mount_point << ") -> "
- << real_mount_point;
- }
- if (!ok && created_mount_point) {
- rmdir(mount_point.c_str());
- }
- errno = saved_errno;
- return ok;
-}
-
-bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {
- auto overlayfs_valid_result = fs_mgr_overlayfs_valid();
- if (overlayfs_valid_result == OverlayfsValidResult::kNotSupported) {
- LERROR << __FUNCTION__ << "(): kernel does not support overlayfs";
- return false;
- }
-
-#if ALLOW_ADBD_DISABLE_VERITY == 0
- // Allowlist the mount point if user build.
- static const std::vector<const std::string> kAllowedPaths = {
- "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
- };
- static const std::vector<const std::string> kAllowedPrefixes = {
- "/mnt/product/",
- "/mnt/vendor/",
- };
- if (std::none_of(kAllowedPaths.begin(), kAllowedPaths.end(),
- [&entry](const auto& path) -> bool {
- return entry.mount_point == path ||
- StartsWith(entry.mount_point, path + "/");
- }) &&
- std::none_of(kAllowedPrefixes.begin(), kAllowedPrefixes.end(),
- [&entry](const auto& prefix) -> bool {
- return entry.mount_point != prefix &&
- StartsWith(entry.mount_point, prefix);
- })) {
- LERROR << __FUNCTION__
- << "(): mount point is forbidden on user build: " << entry.mount_point;
- return false;
- }
-#endif // ALLOW_ADBD_DISABLE_VERITY == 0
-
- if (!fs_mgr_create_canonical_mount_point(entry.mount_point)) {
- return false;
- }
-
- auto options = "lowerdir=" + entry.lowerdir;
- if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
- options += ",override_creds=off";
- }
-
- // Use "overlay-" + entry.blk_device as the mount() source, so that adb-remout-test don't
- // confuse this with adb remount overlay, whose device name is "overlay".
- // Overlayfs is a pseudo filesystem, so the source device is a symbolic value and isn't used to
- // back the filesystem. However the device name would be shown in /proc/mounts.
- auto source = "overlay-" + entry.blk_device;
- auto report = "__mount(source=" + source + ",target=" + entry.mount_point + ",type=overlay," +
- options + ")=";
- auto ret = mount(source.c_str(), entry.mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
- options.c_str());
- if (ret) {
- PERROR << report << ret;
- return false;
- }
- LINFO << report << ret;
- return true;
-}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 75d1e0db6..abece4d7e 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -26,7 +26,7 @@
#include "fs_mgr_priv.h"
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline) {
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
static constexpr char quote = '"';
std::vector<std::pair<std::string, std::string>> result;
@@ -60,50 +60,12 @@ std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std:
return result;
}
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
- const std::string& cmdline) {
- static constexpr char quote = '"';
-
- std::vector<std::pair<std::string, std::string>> result;
- for (auto& line : android::base::Split(cmdline, "\n")) {
- line.erase(std::remove(line.begin(), line.end(), quote), line.end());
- auto equal_sign = line.find('=');
- if (equal_sign == line.npos) {
- if (!line.empty()) {
- // no difference between <key> and <key>=
- result.emplace_back(std::move(line), "");
- }
- } else {
- result.emplace_back(android::base::Trim(line.substr(0, equal_sign)),
- android::base::Trim(line.substr(equal_sign + 1)));
- }
- }
-
- return result;
-}
-
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
- const std::string& android_key, std::string* out_val) {
- FS_MGR_CHECK(out_val != nullptr);
-
- const std::string bootconfig_key("androidboot." + android_key);
- for (const auto& [key, value] : fs_mgr_parse_proc_bootconfig(bootconfig)) {
- if (key == bootconfig_key) {
- *out_val = value;
- return true;
- }
- }
-
- *out_val = "";
- return false;
-}
-
bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
std::string* out_val) {
FS_MGR_CHECK(out_val != nullptr);
const std::string cmdline_key("androidboot." + android_key);
- for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
+ for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
if (key == cmdline_key) {
*out_val = value;
return true;
@@ -114,17 +76,6 @@ bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::s
return false;
}
-// Tries to get the given boot config value from bootconfig.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val) {
- std::string bootconfig;
- if (!android::base::ReadFileToString("/proc/bootconfig", &bootconfig)) return false;
- if (!bootconfig.empty() && bootconfig.back() == '\n') {
- bootconfig.pop_back();
- }
- return fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, out_val);
-}
-
// Tries to get the given boot config value from kernel cmdline.
// Returns true if successfully found, false otherwise.
bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
@@ -159,11 +110,6 @@ bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
return true;
}
- // next, check if we have the property in bootconfig
- if (fs_mgr_get_boot_config_from_bootconfig_source(key, out_val)) {
- return true;
- }
-
// finally, fallback to kernel cmdline, properties may not be ready yet
if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
return true;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 301c90755..fd7386db3 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -121,7 +121,7 @@ static int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_p
}
static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer,
- bool needs_projid, bool needs_casefold, bool fs_compress) {
+ bool needs_projid, bool needs_casefold) {
if (!dev_sz) {
int rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -147,12 +147,6 @@ static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt
args.push_back("-C");
args.push_back("utf8");
}
- if (fs_compress) {
- args.push_back("-O");
- args.push_back("compression");
- args.push_back("-O");
- args.push_back("extra_attr");
- }
args.push_back(fs_blkdev.c_str());
args.push_back(size_str.c_str());
@@ -172,7 +166,7 @@ int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
if (entry.fs_type == "f2fs") {
return format_f2fs(entry.blk_device, entry.length, crypt_footer, needs_projid,
- needs_casefold, entry.fs_mgr_flags.fs_compress);
+ needs_casefold);
} else if (entry.fs_type == "ext4") {
return format_ext4(entry.blk_device, entry.mount_point, crypt_footer, needs_projid,
entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d0c89b909..8a2207870 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -17,7 +17,6 @@
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
-#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -127,16 +126,15 @@ void ParseMountFlags(const std::string& flags, FstabEntry* entry) {
}
fs_options.append(flag);
- if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
- const auto arg = flag.substr(equal_sign + 1);
- if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
- if (!ParseInt(arg, &entry->reserved_size)) {
- LWARNING << "Warning: reserve_root= flag malformed: " << arg;
- } else {
- entry->reserved_size <<= 12;
- }
- } else if (StartsWith(flag, "lowerdir=")) {
- entry->lowerdir = std::move(arg);
+ if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
+ std::string arg;
+ if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+ arg = flag.substr(equal_sign + 1);
+ }
+ if (!ParseInt(arg, &entry->reserved_size)) {
+ LWARNING << "Warning: reserve_root= flag malformed: " << arg;
+ } else {
+ entry->reserved_size <<= 12;
}
}
}
@@ -180,7 +178,6 @@ void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
CheckFlag("slotselect_other", slot_select_other);
CheckFlag("fsverity", fs_verity);
CheckFlag("metadata_csum", ext_meta_csum);
- CheckFlag("fscompress", fs_compress);
#undef CheckFlag
@@ -256,13 +253,6 @@ void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
} else {
entry->reserved_size = static_cast<off64_t>(size);
}
- } else if (StartsWith(flag, "readahead_size_kb=")) {
- int val;
- if (ParseInt(arg, &val, 0, 16 * 1024)) {
- entry->readahead_size_kb = val;
- } else {
- LWARNING << "Warning: readahead_size_kb= flag malformed (0 ~ 16MB): " << arg;
- }
} else if (StartsWith(flag, "eraseblk=")) {
// The erase block size flag is followed by an = and the flash erase block size. Get it,
// check that it is a power of 2 and at least 4096, and return it.
@@ -308,8 +298,7 @@ void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
std::string InitAndroidDtDir() {
std::string android_dt_dir;
// The platform may specify a custom Android DT path in kernel cmdline
- if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
- !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+ if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
// Fall back to the standard procfs-based path
android_dt_dir = kDefaultAndroidDtDir;
}
@@ -422,8 +411,7 @@ std::string GetFstabPath() {
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
- for (const char* prefix :
- {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
+ for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
std::string fstab_path = prefix + suffix;
if (access(fstab_path.c_str(), F_OK) == 0) {
return fstab_path;
@@ -594,19 +582,18 @@ bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
} // namespace
-void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
- const std::vector<std::string>& dsu_partitions) {
+void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions) {
static constexpr char kDsuKeysDir[] = "/avb";
// Convert userdata
// Inherit fstab properties for userdata.
FstabEntry userdata;
if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
userdata = *entry;
- userdata.blk_device = android::gsi::kDsuUserdata;
+ userdata.blk_device = "userdata_gsi";
userdata.fs_mgr_flags.logical = true;
userdata.fs_mgr_flags.formattable = true;
if (!userdata.metadata_key_dir.empty()) {
- userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+ userdata.metadata_key_dir += "/gsi";
}
} else {
userdata = BuildDsuUserdataFstabEntry();
@@ -622,11 +609,7 @@ void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
continue;
}
// userdata has been handled
- if (partition == android::gsi::kDsuUserdata) {
- continue;
- }
- // scratch is handled by fs_mgr_overlayfs
- if (partition == android::gsi::kDsuScratch) {
+ if (StartsWith(partition, "user")) {
continue;
}
// dsu_partition_name = corresponding_partition_name + kDsuPostfix
@@ -651,14 +634,13 @@ void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
entry.fs_mgr_flags.wait = true;
entry.fs_mgr_flags.logical = true;
entry.fs_mgr_flags.first_stage_mount = true;
- fstab->emplace_back(entry);
} else {
// If the corresponding partition exists, transform all its Fstab
// by pointing .blk_device to the DSU partition.
for (auto&& entry : entries) {
entry->blk_device = partition;
// AVB keys for DSU should always be under kDsuKeysDir.
- entry->avb_keys = kDsuKeysDir;
+ entry->avb_keys += kDsuKeysDir;
}
// Make sure the ext4 is included to support GSI.
auto partition_ext4 =
@@ -689,7 +671,7 @@ void EnableMandatoryFlags(Fstab* fstab) {
}
}
-bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
if (!fstab_file) {
PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
@@ -698,51 +680,29 @@ bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
bool is_proc_mounts = path == "/proc/mounts";
- Fstab fstab;
- if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
+ if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, fstab)) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
return false;
}
- if (!is_proc_mounts) {
- if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
- // This is expected to fail if host is android Q, since Q doesn't
- // support DSU slotting. The DSU "active" indicator file would be
- // non-existent or empty if DSU is enabled within the guest system.
- // In that case, just use the default slot name "dsu".
- std::string dsu_slot;
- if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
- PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
- return false;
- }
- if (dsu_slot.empty()) {
- dsu_slot = "dsu";
- LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
- }
- // This file is non-existent on Q vendor.
- std::string lp_names;
- if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
- PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
- return false;
- }
- TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
- } else if (errno != ENOENT) {
- PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
- return false;
- }
+ if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+ std::string lp_names;
+ ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
+ TransformFstabForDsu(fstab, Split(lp_names, ","));
}
- SkipMountingPartitions(&fstab, false /* verbose */);
- EnableMandatoryFlags(&fstab);
+#ifndef NO_SKIP_MOUNT
+ SkipMountingPartitions(fstab);
+#endif
+ EnableMandatoryFlags(fstab);
- *fstab_out = std::move(fstab);
return true;
}
// Returns fstab entries parsed from the device tree if they exist
-bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
+bool ReadFstabFromDt(Fstab* fstab, bool log) {
std::string fstab_buf = ReadFstabFromDt();
if (fstab_buf.empty()) {
- if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
+ if (log) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
return false;
}
@@ -750,36 +710,34 @@ bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
fstab_buf.length(), "r"), fclose);
if (!fstab_file) {
- if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+ if (log) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
return false;
}
if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
- if (verbose) {
+ if (log) {
LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
<< fstab_buf;
}
return false;
}
- SkipMountingPartitions(fstab, verbose);
+#ifndef NO_SKIP_MOUNT
+ SkipMountingPartitions(fstab);
+#endif
return true;
}
-#ifdef NO_SKIP_MOUNT
-bool SkipMountingPartitions(Fstab*, bool) {
- return true;
-}
-#else
+#ifndef NO_SKIP_MOUNT
// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
// only common files for all targets can be put into system partition. It is under
// /system/system_ext because GSI is a single system.img that includes the contents of system_ext
// partition and product partition under /system/system_ext and /system/product, respectively.
-bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
- static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
+bool SkipMountingPartitions(Fstab* fstab) {
+ constexpr const char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
std::string skip_config;
auto save_errno = errno;
@@ -788,39 +746,29 @@ bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
return true;
}
- std::vector<std::string> skip_mount_patterns;
- for (const auto& line : Split(skip_config, "\n")) {
- if (line.empty() || StartsWith(line, "#")) {
+ for (const auto& skip_mount_point : Split(skip_config, "\n")) {
+ if (skip_mount_point.empty()) {
continue;
}
- skip_mount_patterns.push_back(line);
+ auto it = std::remove_if(fstab->begin(), fstab->end(),
+ [&skip_mount_point](const auto& entry) {
+ return entry.mount_point == skip_mount_point;
+ });
+ if (it == fstab->end()) continue;
+ fstab->erase(it, fstab->end());
+ LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
}
- // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry
- // would be partitioned to the second group.
- auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {
- for (const auto& pattern : skip_mount_patterns) {
- if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {
- return false;
- }
- }
- return true;
- };
- auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);
- if (verbose) {
- for (auto it = remove_from; it != fstab->end(); ++it) {
- LINFO << "Skip mounting mountpoint: " << it->mount_point;
- }
- }
- fstab->erase(remove_from, fstab->end());
return true;
}
#endif
// Loads the fstab file and combines with fstab entries passed in from device tree.
bool ReadDefaultFstab(Fstab* fstab) {
- fstab->clear();
- ReadFstabFromDt(fstab, false /* verbose */);
+ Fstab dt_fstab;
+ ReadFstabFromDt(&dt_fstab, false);
+
+ *fstab = std::move(dt_fstab);
std::string default_fstab_path;
// Use different fstab paths for normal boot and recovery boot, respectively
@@ -831,14 +779,16 @@ bool ReadDefaultFstab(Fstab* fstab) {
}
Fstab default_fstab;
- if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
- for (auto&& entry : default_fstab) {
- fstab->emplace_back(std::move(entry));
- }
+ if (!default_fstab_path.empty()) {
+ ReadFstabFromFile(default_fstab_path, &default_fstab);
} else {
LINFO << __FUNCTION__ << "(): failed to find device default fstab";
}
+ for (auto&& entry : default_fstab) {
+ fstab->emplace_back(std::move(entry));
+ }
+
return !fstab->empty();
}
@@ -872,42 +822,15 @@ std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string
}
std::set<std::string> GetBootDevices() {
- // First check bootconfig, then kernel commandline, then the device tree
+ // First check the kernel commandline, then try the device tree otherwise
std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
std::string value;
- if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
- fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
- std::set<std::string> boot_devices;
- // remove quotes and split by spaces
- auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
- for (std::string_view device : boot_device_strings) {
- // trim the trailing comma, keep the rest.
- base::ConsumeSuffix(&device, ",");
- boot_devices.emplace(device);
- }
- return boot_devices;
- }
-
if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
ReadDtFile(dt_file_name, &value)) {
auto boot_devices = Split(value, ",");
return std::set<std::string>(boot_devices.begin(), boot_devices.end());
}
- std::string cmdline;
- if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
- std::set<std::string> boot_devices;
- const std::string cmdline_key = "androidboot.boot_device";
- for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
- if (key == cmdline_key) {
- boot_devices.emplace(value);
- }
- }
- if (!boot_devices.empty()) {
- return boot_devices;
- }
- }
-
// Fallback to extract boot devices from fstab.
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 4d32bda80..1fa1aa104 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -92,6 +92,10 @@ bool fs_mgr_overlayfs_mount_all(Fstab*) {
return false;
}
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
+ return {};
+}
+
bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
if (change) *change = false;
return false;
@@ -109,13 +113,8 @@ bool fs_mgr_overlayfs_is_setup() {
namespace android {
namespace fs_mgr {
-void MapScratchPartitionIfNeeded(Fstab*, const std::function<bool(const std::set<std::string>&)>&) {
-}
-
-void CleanupOldScratchFiles() {}
-
-void TeardownAllOverlayForMountPoint(const std::string&) {}
-
+void MapScratchPartitionIfNeeded(Fstab*,
+ const std::function<bool(const std::set<std::string>&)>&) {}
} // namespace fs_mgr
} // namespace android
@@ -123,38 +122,10 @@ void TeardownAllOverlayForMountPoint(const std::string&) {}
namespace {
-bool fs_mgr_in_recovery() {
- // Check the existence of recovery binary instead of using the compile time
- // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
- // defined, albeit not in recovery. More details: system/core/init/README.md
- return fs_mgr_access("/system/bin/recovery");
-}
-
-bool fs_mgr_is_dsu_running() {
- // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
- // never called in recovery, the return value of android::gsi::IsGsiRunning()
- // is not well-defined. In this case, just return false as being in recovery
- // implies not running a DSU system.
- if (fs_mgr_in_recovery()) return false;
- auto saved_errno = errno;
- auto ret = android::gsi::IsGsiRunning();
- errno = saved_errno;
- return ret;
-}
-
// list of acceptable overlayfs backing storage
const auto kScratchMountPoint = "/mnt/scratch"s;
const auto kCacheMountPoint = "/cache"s;
-
-std::vector<const std::string> OverlayMountPoints() {
- // Never fallback to legacy cache mount point if within a DSU system,
- // because running a DSU system implies the device supports dynamic
- // partitions, which means legacy cache mustn't be used.
- if (fs_mgr_is_dsu_running()) {
- return {kScratchMountPoint};
- }
- return {kScratchMountPoint, kCacheMountPoint};
-}
+const std::vector<const std::string> kOverlayMountPoints = {kScratchMountPoint, kCacheMountPoint};
// Return true if everything is mounted, but before adb is started. Right
// after 'trigger load_persist_props_action' is done.
@@ -192,7 +163,7 @@ bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024; // 8MB
return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
- (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
+ (vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
}
const auto kPhysicalDevice = "/dev/block/by-name/"s;
@@ -200,10 +171,6 @@ constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
// Note: this is meant only for recovery/first-stage init.
bool ScratchIsOnData() {
- // The scratch partition of DSU is managed by gsid.
- if (fs_mgr_is_dsu_running()) {
- return false;
- }
return fs_mgr_access(kScratchImageMetadata);
}
@@ -307,7 +274,7 @@ const auto kOverlayTopDir = "/overlay"s;
std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
if (!fs_mgr_is_dir(mount_point)) return "";
const auto base = android::base::Basename(mount_point) + "/";
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
auto upper = dir + kUpperName;
if (!fs_mgr_is_dir(upper)) continue;
@@ -497,12 +464,6 @@ bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change)
// umount and delete kScratchMountPoint storage if we have logical partitions
if (overlay != kScratchMountPoint) return true;
- // Validation check.
- if (fs_mgr_is_dsu_running()) {
- LERROR << "Destroying DSU scratch is not allowed.";
- return false;
- }
-
auto save_errno = errno;
if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
fs_mgr_overlayfs_umount_scratch();
@@ -551,13 +512,10 @@ bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change)
}
bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
- bool* change, bool* should_destroy_scratch = nullptr) {
+ bool* change) {
const auto top = overlay + kOverlayTopDir;
- if (!fs_mgr_access(top)) {
- if (should_destroy_scratch) *should_destroy_scratch = true;
- return true;
- }
+ if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
auto cleanup_all = mount_point.empty();
const auto partition_name = android::base::Basename(mount_point);
@@ -613,7 +571,7 @@ bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string
PERROR << "rmdir " << top;
}
}
- if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
+ if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
return ret;
}
@@ -851,26 +809,15 @@ bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::s
entry.fs_type = mnt_type;
if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
- entry.flags = MS_NOATIME | MS_RDONLY;
- auto mounted = true;
- if (!readonly) {
- if (entry.fs_type == "ext4") {
- // check if ext4 de-dupe
- entry.flags |= MS_RDONLY;
- auto save_errno = errno;
- mounted = fs_mgr_do_mount_one(entry) == 0;
- if (mounted) {
- mounted = !fs_mgr_has_shared_blocks(entry.mount_point, entry.blk_device);
- fs_mgr_overlayfs_umount_scratch();
- }
- errno = save_errno;
- }
- entry.flags &= ~MS_RDONLY;
+ entry.flags = MS_NOATIME;
+ if (readonly) {
+ entry.flags |= MS_RDONLY;
+ } else {
fs_mgr_set_blk_ro(device_path, false);
}
entry.fs_mgr_flags.check = true;
auto save_errno = errno;
- if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
+ auto mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
if ((entry.fs_type == "f2fs") && ext4) {
entry.fs_type = "ext4";
@@ -923,29 +870,12 @@ static std::string GetPhysicalScratchDevice() {
return "";
}
-// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
-// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
-static std::string GetDsuScratchDevice() {
- auto& dm = DeviceMapper::Instance();
- std::string device;
- if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
- dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
- return device;
- }
- return "";
-}
-
// This returns the scratch device that was detected during early boot (first-
// stage init). If the device was created later, for example during setup for
// the adb remount command, it can return an empty string since it does not
// query ImageManager. (Note that ImageManager in first-stage init will always
// use device-mapper, since /data is not available to use loop devices.)
static std::string GetBootScratchDevice() {
- // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
- if (fs_mgr_is_dsu_running()) {
- return GetDsuScratchDevice();
- }
-
auto& dm = DeviceMapper::Instance();
// If there is a scratch partition allocated in /data or on super, we
@@ -1104,7 +1034,7 @@ static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_ex
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
- if (change) *change = false;
+ *change = false;
auto images = IImageManager::Open("remount", 10s);
if (!images) {
@@ -1124,7 +1054,7 @@ static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exi
return false;
}
- if (change) *change = true;
+ *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
// clean_scratch_files and accidentally try to map an image that will be
@@ -1167,14 +1097,6 @@ static bool CanUseSuperPartition(const Fstab& fstab, bool* is_virtual_ab) {
bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
bool* partition_exists, bool* change) {
- // Use the DSU scratch device managed by gsid if within a DSU system.
- if (fs_mgr_is_dsu_running()) {
- *scratch_device = GetDsuScratchDevice();
- *partition_exists = !scratch_device->empty();
- *change = false;
- return *partition_exists;
- }
-
// Try a physical partition first.
*scratch_device = GetPhysicalScratchDevice();
if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
@@ -1233,8 +1155,12 @@ bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab, bool* change) {
bool fs_mgr_overlayfs_invalid() {
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
- // in recovery or fastbootd, not allowed!
- return fs_mgr_in_recovery();
+ // in recovery, fastbootd, or gsi mode, not allowed!
+ if (fs_mgr_access("/system/bin/recovery")) return true;
+ auto save_errno = errno;
+ auto ret = android::gsi::IsGsiRunning();
+ errno = save_errno;
+ return ret;
}
} // namespace
@@ -1351,7 +1277,7 @@ bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool*
if (candidates.empty()) return ret;
std::string dir;
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
if (backing && backing[0] && (overlay_mount_point != backing)) continue;
if (overlay_mount_point == kScratchMountPoint) {
if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
@@ -1377,8 +1303,6 @@ bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool*
return ret;
}
-// Note: This function never returns the DSU scratch device in recovery or fastbootd,
-// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
static bool EnsureScratchMapped(std::string* device, bool* mapped) {
*mapped = false;
*device = GetBootScratchDevice();
@@ -1386,11 +1310,6 @@ static bool EnsureScratchMapped(std::string* device, bool* mapped) {
return true;
}
- if (!fs_mgr_in_recovery()) {
- errno = EINVAL;
- return false;
- }
-
auto partition_name = android::base::Basename(kScratchMountPoint);
// Check for scratch on /data first, before looking for a modified super
@@ -1432,27 +1351,10 @@ static bool EnsureScratchMapped(std::string* device, bool* mapped) {
return true;
}
-// This should only be reachable in recovery, where DSU scratch is not
-// automatically mapped.
-static bool MapDsuScratchDevice(std::string* device) {
- std::string dsu_slot;
- if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
- dsu_slot.empty()) {
- // Nothing to do if no DSU installation present.
- return false;
- }
-
- auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
- if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
- // Nothing to do if DSU scratch device doesn't exist.
- return false;
- }
-
- images->UnmapImageDevice(android::gsi::kDsuScratch);
- if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
- return false;
- }
- return true;
+static void UnmapScratchDevice() {
+ // This should only be reachable in recovery, where scratch is not
+ // automatically mapped and therefore can be unmapped.
+ DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
}
// Returns false if teardown not permitted, errno set to last error.
@@ -1464,27 +1366,21 @@ bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
// If scratch exists, but is not mounted, lets gain access to clean
// specific override entries.
auto mount_scratch = false;
+ bool unmap = false;
if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
- std::string scratch_device = GetBootScratchDevice();
- if (!scratch_device.empty()) {
+ std::string scratch_device;
+ if (EnsureScratchMapped(&scratch_device, &unmap)) {
mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
fs_mgr_overlayfs_scratch_mount_type());
}
}
- bool should_destroy_scratch = false;
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ for (const auto& overlay_mount_point : kOverlayMountPoints) {
ret &= fs_mgr_overlayfs_teardown_one(
- overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change,
- overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
- }
- // Do not attempt to destroy DSU scratch if within a DSU system,
- // because DSU scratch partition is managed by gsid.
- if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
- ret &= fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, change);
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
}
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
// After obligatory teardown to make sure everything is clean, but if
- // we didn't want overlayfs in the first place, we do not want to
+ // we didn't want overlayfs in the the first place, we do not want to
// waste time on a reboot (or reboot request message).
if (change) *change = false;
}
@@ -1498,6 +1394,9 @@ bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (mount_scratch) {
fs_mgr_overlayfs_umount_scratch();
}
+ if (unmap) {
+ UnmapScratchDevice();
+ }
return ret;
}
@@ -1565,54 +1464,6 @@ void CleanupOldScratchFiles() {
}
}
-void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
- if (!fs_mgr_in_recovery()) {
- LERROR << __FUNCTION__ << "(): must be called within recovery.";
- return;
- }
-
- // Empty string means teardown everything.
- const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
- constexpr bool* ignore_change = nullptr;
-
- // Teardown legacy overlay mount points that's not backed by a scratch device.
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- if (overlay_mount_point == kScratchMountPoint) {
- continue;
- }
- fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
- }
-
- // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
- bool mapped = false;
- std::string scratch_device;
- if (EnsureScratchMapped(&scratch_device, &mapped)) {
- fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
- bool should_destroy_scratch = false;
- fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
- &should_destroy_scratch);
- if (should_destroy_scratch) {
- fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
- }
- fs_mgr_overlayfs_umount_scratch();
- }
- if (mapped) {
- DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
- }
- }
-
- // Teardown DSU overlay if present.
- if (MapDsuScratchDevice(&scratch_device)) {
- fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
- fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
- fs_mgr_overlayfs_umount_scratch();
- }
- DestroyLogicalPartition(android::gsi::kDsuScratch);
- }
-}
-
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index 6a3840131..417fb380b 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -22,16 +22,11 @@
#include <utility>
#include <vector>
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline);
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
std::string* out_val);
bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
- const std::string& bootconfig);
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig, const std::string& key,
- std::string* out_val);
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val);
#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index e685070a8..def1c2178 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -16,6 +16,7 @@
#include <errno.h>
#include <getopt.h>
+#include <libavb_user/libavb_user.h>
#include <stdio.h>
#include <sys/mount.h>
#include <sys/types.h>
@@ -39,8 +40,6 @@
#include <fs_mgr_overlayfs.h>
#include <fs_mgr_priv.h>
#include <fstab/fstab.h>
-#include <libavb_user/libavb_user.h>
-#include <libgsi/libgsid.h>
namespace {
@@ -53,9 +52,7 @@ namespace {
"\tpartition\tspecific partition(s) (empty does all)\n"
"\n"
"Remount specified partition(s) read-write, by name or mount point.\n"
- "-R notwithstanding, verity must be disabled on partition(s).\n"
- "-R within a DSU guest system reboots into the DSU instead of the host system,\n"
- "this command would enable DSU (one-shot) if not already enabled.";
+ "-R notwithstanding, verity must be disabled on partition(s).";
::exit(exit_status);
}
@@ -126,26 +123,23 @@ static android::sp<android::os::IVold> GetVold() {
using namespace std::chrono_literals;
-enum RemountStatus {
- REMOUNT_SUCCESS = 0,
- NOT_USERDEBUG,
- BADARG,
- NOT_ROOT,
- NO_FSTAB,
- UNKNOWN_PARTITION,
- INVALID_PARTITION,
- VERITY_PARTITION,
- BAD_OVERLAY,
- NO_MOUNTS,
- REMOUNT_FAILED,
- MUST_REBOOT,
- BINDER_ERROR,
- CHECKPOINTING,
- GSID_ERROR,
-};
-
static int do_remount(int argc, char* argv[]) {
- RemountStatus retval = REMOUNT_SUCCESS;
+ enum {
+ SUCCESS = 0,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ MUST_REBOOT,
+ BINDER_ERROR,
+ CHECKPOINTING
+ } retval = SUCCESS;
// If somehow this executable is delivered on a "user" build, it can
// not function, so providing a clear message to the caller rather than
@@ -344,45 +338,11 @@ static int do_remount(int argc, char* argv[]) {
++it;
}
- // If (1) remount requires a reboot to take effect, (2) system is currently
- // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
- // next reboot would not take us back to the host system but stay within
- // the guest system.
- if (reboot_later) {
- if (auto gsid = android::gsi::GetGsiService()) {
- auto dsu_running = false;
- if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
- LOG(ERROR) << "Failed to get DSU running state: " << status;
- return BINDER_ERROR;
- }
- auto dsu_enabled = false;
- if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
- LOG(ERROR) << "Failed to get DSU enabled state: " << status;
- return BINDER_ERROR;
- }
- if (dsu_running && !dsu_enabled) {
- std::string dsu_slot;
- if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
- LOG(ERROR) << "Failed to get active DSU slot: " << status;
- return BINDER_ERROR;
- }
- LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
- "DSU guest system after reboot";
- int error = 0;
- if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
- !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
- LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
- return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
- }
- LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
- }
- }
- }
-
if (partitions.empty() || just_disabled_verity) {
if (reboot_later) reboot(setup_overlayfs);
if (user_please_reboot_later) {
- return MUST_REBOOT;
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
}
LOG(WARNING) << "No partitions to remount";
return retval;
@@ -411,26 +371,19 @@ static int do_remount(int argc, char* argv[]) {
auto blk_device = entry.blk_device;
auto mount_point = entry.mount_point;
- auto found = false;
for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
auto& rentry = *it;
if (mount_point == rentry.mount_point) {
blk_device = rentry.blk_device;
- found = true;
break;
}
// Find overlayfs mount point?
if ((mount_point == "/") && (rentry.mount_point == "/system")) {
blk_device = rentry.blk_device;
mount_point = "/system";
- found = true;
break;
}
}
- if (!found) {
- PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
- continue;
- }
if (blk_device == "/dev/root") {
auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
if (from_fstab) blk_device = from_fstab->blk_device;
@@ -478,12 +431,6 @@ static int do_remount(int argc, char* argv[]) {
int main(int argc, char* argv[]) {
android::base::InitLogging(argv, MyLogger);
int result = do_remount(argc, argv);
- if (result == MUST_REBOOT) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- } else if (result == REMOUNT_SUCCESS) {
- printf("remount succeeded\n");
- } else {
- printf("remount failed\n");
- }
+ printf("remount %s\n", result ? "failed" : "succeeded");
return result;
}
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index fdaffbeb0..1e655877f 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -111,8 +111,7 @@ bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string&
return true;
}
- static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "erofs",
- "none"};
+ static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
return false;
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 1372511a7..830f0dd01 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -92,7 +92,7 @@ bool fs_mgr_vendor_overlay_mount(const std::pair<std::string, std::string>& moun
}
auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
options + ")=";
- auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+ auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
options.c_str());
if (ret) {
PERROR << report << ret;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 4d3ecc9dc..2a67b8c9f 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -60,20 +60,8 @@ enum mount_mode {
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL (-1)
-
-struct MountAllResult {
- // One of the FS_MGR_MNTALL_* returned code defined above.
- int code;
- // Whether userdata was mounted as a result of |fs_mgr_mount_all| call.
- bool userdata_mounted;
-};
-
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
-// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
-// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
-// for mounting userdata. Later is required for init to correctly enqueue fs-related events as part
-// of userdata remount during userspace reboot.
-MountAllResult fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
+int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
@@ -88,10 +76,6 @@ int fs_mgr_do_tmpfs_mount(const char *n_name);
bool fs_mgr_load_verity_state(int* mode);
// Returns true if verity is enabled on this particular FstabEntry.
bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
-// Returns the hash algorithm used to build the hashtree of this particular FstabEntry. Returns an
-// empty string if the input isn't a dm-verity entry, or if there is an error.
-std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry);
-
bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
@@ -131,12 +115,3 @@ int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab);
// Finds the dm_bow device on which this block device is stacked, or returns
// empty string
std::string fs_mgr_find_bow_device(const std::string& block_device);
-
-// Creates mount point if not already existed, and checks that mount point is a
-// canonical path that doesn't contain any symbolic link or /../.
-bool fs_mgr_create_canonical_mount_point(const std::string& mount_point);
-
-// Like fs_mgr_do_mount_one() but for overlayfs fstab entries.
-// Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
-// filesystem cannot be remount read-write.
-bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 6caab1fee..34aded99e 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -27,6 +27,7 @@
android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr, bool force = true);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
@@ -48,12 +49,5 @@ void MapScratchPartitionIfNeeded(Fstab* fstab,
const std::function<bool(const std::set<std::string>&)>& init);
void CleanupOldScratchFiles();
-// Teardown overlays of all sources (cache dir, scratch device, DSU) for |mount_point|.
-// Teardown all overlays if |mount_point| is empty.
-//
-// Note: This should be called if and only if in recovery or fastbootd to teardown
-// overlays if any partition is flashed or updated.
-void TeardownAllOverlayForMountPoint(const std::string& mount_point = {});
-
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f33768b9d..7cf4f8997 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -47,7 +47,6 @@ struct FstabEntry {
int max_comp_streams = 0;
off64_t zram_size = 0;
off64_t reserved_size = 0;
- off64_t readahead_size_kb = -1;
std::string encryption_options;
off64_t erase_blk_size = 0;
off64_t logical_blk_size = 0;
@@ -55,7 +54,6 @@ struct FstabEntry {
std::string vbmeta_partition;
uint64_t zram_backingdev_size = 0;
std::string avb_keys;
- std::string lowerdir;
struct FsMgrFlags {
bool wait : 1;
@@ -85,7 +83,6 @@ struct FstabEntry {
bool slot_select_other : 1;
bool fs_verity : 1;
bool ext_meta_csum : 1;
- bool fs_compress : 1;
} fs_mgr_flags = {};
bool is_encryptable() const {
@@ -99,9 +96,9 @@ struct FstabEntry {
using Fstab = std::vector<FstabEntry>;
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
-bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
+bool ReadFstabFromDt(Fstab* fstab, bool log = true);
bool ReadDefaultFstab(Fstab* fstab);
-bool SkipMountingPartitions(Fstab* fstab, bool verbose = false);
+bool SkipMountingPartitions(Fstab* fstab);
FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
// The Fstab can contain multiple entries for the same mount point with different configurations.
@@ -115,8 +112,7 @@ std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string
// dsu_partitions[0] = "system_gsi"
// dsu_partitions[1] = "userdata_gsi"
// dsu_partitions[2] = ...
-void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
- const std::vector<std::string>& dsu_partitions);
+void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions);
std::set<std::string> GetBootDevices();
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 428a7f4ec..58241b3f8 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libdm",
defaults: ["fs_mgr_defaults"],
@@ -46,8 +42,6 @@ cc_library_static {
enabled: false,
},
},
- ramdisk_available: true,
- vendor_ramdisk_available: true,
}
filegroup {
@@ -104,3 +98,7 @@ cc_fuzz {
"liblog",
],
}
+
+vts_config {
+ name: "VtsKernelLibdmTest",
+}
diff --git a/fs_mgr/libdm/AndroidTest.xml b/fs_mgr/libdm/AndroidTest.xml
new file mode 100644
index 000000000..b4e0c230e
--- /dev/null
+++ b/fs_mgr/libdm/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelLibdmTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsKernelLibdmTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/libdm_test/libdm_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/libdm_test/libdm_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ <option name="precondition-first-api-level" value="29" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index e43c00b44..791268849 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -35,10 +35,6 @@
#include "utility.h"
-#ifndef DM_DEFERRED_REMOVE
-#define DM_DEFERRED_REMOVE (1 << 17)
-#endif
-
namespace android {
namespace dm {
@@ -115,10 +111,8 @@ bool DeviceMapper::DeleteDevice(const std::string& name,
// Check to make sure appropriate uevent is generated so ueventd will
// do the right thing and remove the corresponding device node and symlinks.
- if ((io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
- LOG(ERROR) << "Didn't generate uevent for [" << name << "] removal";
- return false;
- }
+ CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
+ << "Didn't generate uevent for [" << name << "] removal";
if (timeout_ms <= std::chrono::milliseconds::zero()) {
return true;
@@ -137,25 +131,6 @@ bool DeviceMapper::DeleteDevice(const std::string& name) {
return DeleteDevice(name, 0ms);
}
-bool DeviceMapper::DeleteDeviceDeferred(const std::string& name) {
- struct dm_ioctl io;
- InitIo(&io, name);
-
- io.flags |= DM_DEFERRED_REMOVE;
- if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
- PLOG(ERROR) << "DM_DEV_REMOVE with DM_DEFERRED_REMOVE failed for [" << name << "]";
- return false;
- }
- return true;
-}
-
-bool DeviceMapper::DeleteDeviceIfExistsDeferred(const std::string& name) {
- if (GetState(name) == DmDeviceState::INVALID) {
- return true;
- }
- return DeleteDeviceDeferred(name);
-}
-
static std::string GenerateUuid() {
uuid_t uuid_bytes;
uuid_generate(uuid_bytes);
@@ -452,20 +427,6 @@ bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* p
return true;
}
-// Accepts a device mapper device name (like system_a, vendor_b etc) and
-// returns its UUID.
-bool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {
- struct dm_ioctl io;
- InitIo(&io, name);
- if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
- PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
- return false;
- }
-
- *uuid = std::string(io.uuid);
- return true;
-}
-
bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
struct dm_ioctl io;
InitIo(&io, name);
@@ -560,30 +521,34 @@ std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
return std::string{spec.target_type, sizeof(spec.target_type)};
}
-std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
+static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
static constexpr std::string_view kDevBlockPrefix("/dev/block/");
if (android::base::StartsWith(path, kDevBlockPrefix)) {
- return path.substr(kDevBlockPrefix.length());
+ *name = path.substr(kDevBlockPrefix.length());
+ return true;
}
- return {};
+ return false;
}
bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
- std::optional<std::string> name = ExtractBlockDeviceName(path);
- return name && android::base::StartsWith(*name, "dm-");
+ std::string name;
+ if (!ExtractBlockDeviceName(path, &name)) {
+ return false;
+ }
+ return android::base::StartsWith(name, "dm-");
}
std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
- std::optional<std::string> name = ExtractBlockDeviceName(path);
- if (!name) {
+ std::string name;
+ if (!ExtractBlockDeviceName(path, &name)) {
LOG(WARNING) << path << " is not a block device";
return std::nullopt;
}
- if (!android::base::StartsWith(*name, "dm-")) {
+ if (!android::base::StartsWith(name, "dm-")) {
LOG(WARNING) << path << " is not a dm device";
return std::nullopt;
}
- std::string dm_name_file = "/sys/block/" + *name + "/dm/name";
+ std::string dm_name_file = "/sys/block/" + name + "/dm/name";
std::string dm_name;
if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
PLOG(ERROR) << "Failed to read file " << dm_name_file;
@@ -594,16 +559,16 @@ std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string
}
std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
- std::optional<std::string> name = ExtractBlockDeviceName(path);
- if (!name) {
+ std::string name;
+ if (!ExtractBlockDeviceName(path, &name)) {
LOG(WARNING) << path << " is not a block device";
return std::nullopt;
}
- if (!android::base::StartsWith(*name, "dm-")) {
+ if (!android::base::StartsWith(name, "dm-")) {
// Reached bottom of the device mapper stack.
return std::nullopt;
}
- auto slaves_dir = "/sys/block/" + *name + "/slaves";
+ auto slaves_dir = "/sys/block/" + name + "/slaves";
auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
if (dir == nullptr) {
PLOG(ERROR) << "Failed to open: " << slaves_dir;
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index b0639e611..250cb82c5 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -95,9 +95,7 @@ void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint3
}
void DmTargetVerity::SetVerityMode(const std::string& mode) {
- if (mode != "panic_on_corruption" &&
- mode != "restart_on_corruption" &&
- mode != "ignore_corruption") {
+ if (mode != "restart_on_corruption" && mode != "ignore_corruption") {
LOG(ERROR) << "Unknown verity mode: " << mode;
valid_ = false;
return;
@@ -282,13 +280,5 @@ std::string DmTargetDefaultKey::GetParameterString() const {
return android::base::Join(argv, " ");
}
-std::string DmTargetUser::GetParameterString() const {
- std::vector<std::string> argv;
- argv.push_back(std::to_string(start()));
- argv.push_back(std::to_string(size()));
- argv.push_back(control_device());
- return android::base::Join(argv, " ");
-}
-
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 8006db220..41d3145bb 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -35,7 +35,6 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include "test_util.h"
-#include "utility.h"
using namespace std;
using namespace std::chrono_literals;
@@ -618,64 +617,3 @@ TEST(libdm, GetParentBlockDeviceByPath) {
auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path());
ASSERT_EQ(loop.device(), *sub_block_device);
}
-
-TEST(libdm, DeleteDeviceDeferredNoReferences) {
- unique_fd tmp(CreateTempFile("file_1", 4096));
- ASSERT_GE(tmp, 0);
- LoopDevice loop(tmp, 10s);
- ASSERT_TRUE(loop.valid());
-
- DmTable table;
- ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
- ASSERT_TRUE(table.valid());
- TempDevice dev("libdm-test-dm-linear", table);
- ASSERT_TRUE(dev.valid());
-
- DeviceMapper& dm = DeviceMapper::Instance();
-
- std::string path;
- ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
- ASSERT_EQ(0, access(path.c_str(), F_OK));
-
- ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear"));
-
- ASSERT_TRUE(WaitForFileDeleted(path, 5s));
- ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
- ASSERT_NE(0, access(path.c_str(), F_OK));
- ASSERT_EQ(ENOENT, errno);
-}
-
-TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) {
- unique_fd tmp(CreateTempFile("file_1", 4096));
- ASSERT_GE(tmp, 0);
- LoopDevice loop(tmp, 10s);
- ASSERT_TRUE(loop.valid());
-
- DmTable table;
- ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
- ASSERT_TRUE(table.valid());
- TempDevice dev("libdm-test-dm-linear", table);
- ASSERT_TRUE(dev.valid());
-
- DeviceMapper& dm = DeviceMapper::Instance();
-
- std::string path;
- ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
- ASSERT_EQ(0, access(path.c_str(), F_OK));
-
- {
- // Open a reference to block device.
- unique_fd fd(TEMP_FAILURE_RETRY(open(dev.path().c_str(), O_RDONLY | O_CLOEXEC)));
- ASSERT_GE(fd.get(), 0);
-
- ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear"));
-
- ASSERT_EQ(0, access(path.c_str(), F_OK));
- }
-
- // After release device will be removed.
- ASSERT_TRUE(WaitForFileDeleted(path, 5s));
- ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
- ASSERT_NE(0, access(path.c_str(), F_OK));
- ASSERT_EQ(ENOENT, errno);
-}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index bdbbf9112..abe9c4cda 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -49,10 +49,6 @@ enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
static constexpr uint64_t kSectorSize = 512;
-// Returns `path` without /dev/block prefix if and only if `path` starts with
-// that prefix.
-std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
-
class DeviceMapper final {
public:
class DmBlockDevice final {
@@ -99,12 +95,6 @@ class DeviceMapper final {
bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms);
- // Enqueues a deletion of device mapper device with the given name once last reference is
- // closed.
- // Returns 'true' on success, false otherwise.
- bool DeleteDeviceDeferred(const std::string& name);
- bool DeleteDeviceIfExistsDeferred(const std::string& name);
-
// Fetches and returns the complete state of the underlying device mapper
// device with given name.
std::optional<Info> GetDetailedInfo(const std::string& name) const;
@@ -182,13 +172,6 @@ class DeviceMapper final {
// could race with ueventd.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
- // Returns the device mapper UUID for a given name. If the device does not
- // exist, false is returned, and the path parameter is not set.
- //
- // WaitForFile() should not be used in conjunction with this call, since it
- // could race with ueventd.
- bool GetDmDeviceUuidByName(const std::string& name, std::string* path);
-
// Returns a device's unique path as generated by ueventd. This will return
// true as long as the device has been created, even if ueventd has not
// processed it yet.
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 478a3c69c..f986cfee0 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -309,19 +309,6 @@ class DmTargetDefaultKey final : public DmTarget {
bool is_hw_wrapped_ = false;
};
-class DmTargetUser final : public DmTarget {
- public:
- DmTargetUser(uint64_t start, uint64_t length, std::string control_device)
- : DmTarget(start, length), control_device_(control_device) {}
-
- std::string name() const override { return "user"; }
- std::string control_device() const { return control_device_; }
- std::string GetParameterString() const override;
-
- private:
- std::string control_device_;
-};
-
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 1c5872e6f..976cd9033 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libfiemap_headers",
recovery_available: true,
@@ -49,7 +45,6 @@ cc_defaults {
whole_static_libs: [
"gsi_aidl_interface-cpp",
"libgsi",
- "libgsid",
],
shared_libs: [
"libbinder",
@@ -95,7 +90,33 @@ cc_test {
cc_test {
name: "fiemap_image_test",
static_libs: [
+ "libdm",
+ "libext4_utils",
+ "libfs_mgr",
+ "liblp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
"libcrypto_utils",
+ "libcutils",
+ "liblog",
+ ],
+ srcs: [
+ "image_test.cpp",
+ ],
+ test_suites: ["device-tests"],
+ auto_gen_config: true,
+ require_root: true,
+}
+
+/* BUG(148874852) temporary test */
+cc_test {
+ name: "fiemap_image_test_presubmit",
+ cppflags: [
+ "-DSKIP_TEST_IN_PRESUBMIT",
+ ],
+ static_libs: [
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -104,6 +125,7 @@ cc_test {
shared_libs: [
"libbase",
"libcrypto",
+ "libcrypto_utils",
"libcutils",
"liblog",
],
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 31a57a800..5e29d4e26 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -19,9 +19,9 @@
#include <android-base/properties.h>
#include <android/gsi/BnProgressCallback.h>
#include <android/gsi/IGsiService.h>
+#include <binder/IServiceManager.h>
#include <libfiemap/image_manager.h>
#include <libgsi/libgsi.h>
-#include <libgsi/libgsid.h>
namespace android {
namespace fiemap {
@@ -224,10 +224,19 @@ bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::str
return false;
}
-std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir,
- const std::chrono::milliseconds& /*timeout_ms*/,
- const DeviceInfo&) {
- android::sp<IGsiService> service = android::gsi::GetGsiService();
+static sp<IGsiService> GetGsiService() {
+ auto sm = android::defaultServiceManager();
+ auto name = android::String16(kGsiServiceName);
+ android::sp<android::IBinder> res = sm->waitForService(name);
+ if (res) {
+ return android::interface_cast<IGsiService>(res);
+ }
+ return nullptr;
+}
+
+std::unique_ptr<IImageManager> IImageManager::Open(
+ const std::string& dir, const std::chrono::milliseconds& /*timeout_ms*/) {
+ android::sp<IGsiService> service = GetGsiService();
android::sp<IImageService> manager;
auto status = service->openImageService(dir, &manager);
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 8acb88536..4dd4bcc14 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -45,14 +45,14 @@ namespace fiemap {
using namespace android::dm;
-// We cap the maximum number of extents as a robustness measure.
+// We cap the maximum number of extents as a sanity measure.
static constexpr uint32_t kMaxExtents = 50000;
// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
static constexpr const uint32_t kUnsupportedExtentFlags =
FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
- FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED;
+ FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
// Large file support must be enabled.
static_assert(sizeof(off_t) == sizeof(uint64_t));
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index dcbbc5448..3ee742f5e 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -16,8 +16,6 @@
#include <libfiemap/image_manager.h>
-#include <optional>
-
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -55,8 +53,7 @@ using android::fs_mgr::GetPartitionName;
static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
-std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix,
- const DeviceInfo& device_info) {
+std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
auto metadata_dir = "/metadata/gsi/" + dir_prefix;
auto data_dir = "/data/gsi/" + dir_prefix;
auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));
@@ -64,28 +61,17 @@ std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix,
if (ReadFileToString(install_dir_file, &path)) {
data_dir = path;
}
- return Open(metadata_dir, data_dir, device_info);
+ return Open(metadata_dir, data_dir);
}
std::unique_ptr<ImageManager> ImageManager::Open(const std::string& metadata_dir,
- const std::string& data_dir,
- const DeviceInfo& device_info) {
- return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir, device_info));
+ const std::string& data_dir) {
+ return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir));
}
-ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir,
- const DeviceInfo& device_info)
- : metadata_dir_(metadata_dir), data_dir_(data_dir), device_info_(device_info) {
+ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir)
+ : metadata_dir_(metadata_dir), data_dir_(data_dir) {
partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
-
- // Allow overriding whether ImageManager thinks it's in recovery, for testing.
-#ifdef __ANDROID_RECOVERY__
- device_info_.is_recovery = {true};
-#else
- if (!device_info_.is_recovery.has_value()) {
- device_info_.is_recovery = {false};
- }
-#endif
}
std::string ImageManager::GetImageHeaderPath(const std::string& name) {
@@ -150,13 +136,13 @@ bool ImageManager::BackingImageExists(const std::string& name) {
return !!FindPartition(*metadata.get(), name);
}
-bool ImageManager::MetadataDirIsTest() const {
- return IsSubdir(metadata_dir_, kTestImageMetadataDir) ||
- IsSubdir(metadata_dir_, kOtaTestImageMetadataDir);
+static bool IsTestDir(const std::string& path) {
+ return android::base::StartsWith(path, kTestImageMetadataDir) ||
+ android::base::StartsWith(path, kOtaTestImageMetadataDir);
}
-bool ImageManager::IsUnreliablePinningAllowed() const {
- return IsSubdir(data_dir_, "/data/gsi/dsu/") || MetadataDirIsTest();
+static bool IsUnreliablePinningAllowed(const std::string& path) {
+ return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
}
FiemapStatus ImageManager::CreateBackingImage(
@@ -173,7 +159,7 @@ FiemapStatus ImageManager::CreateBackingImage(
if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
return FiemapStatus::Error();
}
- if (!reliable_pinning && !IsUnreliablePinningAllowed()) {
+ if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
// For historical reasons, we allow unreliable pinning for certain use
// cases (DSUs, testing) because the ultimate use case is either
// developer-oriented or ephemeral (the intent is to boot immediately
@@ -192,7 +178,7 @@ FiemapStatus ImageManager::CreateBackingImage(
// if device-mapper is stacked in some complex way not supported by
// FiemapWriter.
auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") && !MetadataDirIsTest()) {
+ if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
fw = {};
@@ -273,11 +259,10 @@ bool ImageManager::DeleteBackingImage(const std::string& name) {
return false;
}
- if (device_info_.is_recovery.value()) {
- LOG(ERROR) << "Cannot remove images backed by /data in recovery";
- return false;
- }
-
+#if defined __ANDROID_RECOVERY__
+ LOG(ERROR) << "Cannot remove images backed by /data in recovery";
+ return false;
+#else
std::string message;
auto header_file = GetImageHeaderPath(name);
if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
@@ -291,6 +276,7 @@ bool ImageManager::DeleteBackingImage(const std::string& name) {
LOG(ERROR) << "Error removing " << status_file << ": " << message;
}
return RemoveImageMetadata(metadata_dir_, name);
+#endif
}
// Create a block device for an image file, using its extents in its
@@ -500,14 +486,15 @@ bool ImageManager::MapWithLoopDevice(const std::string& name,
if (!MapWithLoopDeviceList(loop_devices, name, timeout_ms, path)) {
return false;
}
- } else {
- auto status_message = "loop:" + loop_devices.back();
- auto status_file = GetStatusFilePath(name);
- if (!android::base::WriteStringToFile(status_message, status_file)) {
- PLOG(ERROR) << "Write failed: " << status_file;
- return false;
- }
}
+
+ auto status_message = "loop:" + loop_devices.back();
+ auto status_file = GetStatusFilePath(name);
+ if (!android::base::WriteStringToFile(status_message, status_file)) {
+ PLOG(ERROR) << "Write failed: " << status_file;
+ return false;
+ }
+
auto_detach.Commit();
*path = loop_devices.back();
@@ -533,9 +520,6 @@ bool ImageManager::MapImageDevice(const std::string& name,
// filesystem. This should only happen on devices with no encryption, or
// devices with FBE and no metadata encryption. For these cases it suffices
// to perform normal file writes to /data/gsi (which is unencrypted).
- //
- // Note: this is not gated on DeviceInfo, because the recovery-specific path
- // must only be used in actual recovery.
std::string block_device;
bool can_use_devicemapper;
if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
@@ -591,7 +575,7 @@ bool ImageManager::UnmapImageDevice(const std::string& name, bool force) {
return false;
}
auto& dm = DeviceMapper::Instance();
- std::optional<LoopControl> loop;
+ LoopControl loop;
std::string status;
auto status_file = GetStatusFilePath(name);
@@ -615,14 +599,9 @@ bool ImageManager::UnmapImageDevice(const std::string& name, bool force) {
return false;
}
} else if (pieces[0] == "loop") {
- // Lazily connect to loop-control to avoid spurious errors in recovery.
- if (!loop.has_value()) {
- loop.emplace();
- }
-
// Failure to remove a loop device is not fatal, since we can still
// remove the backing file if we want.
- loop->Detach(pieces[1]);
+ loop.Detach(pieces[1]);
} else {
LOG(ERROR) << "Unknown status: " << pieces[0];
}
@@ -661,22 +640,16 @@ bool ImageManager::Validate() {
return false;
}
- bool ok = true;
for (const auto& partition : metadata->partitions) {
auto name = GetPartitionName(partition);
auto image_path = GetImageHeaderPath(name);
auto fiemap = SplitFiemap::Open(image_path);
- if (fiemap == nullptr) {
- LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
- ok = false;
- continue;
- }
- if (!fiemap->HasPinnedExtents()) {
- LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
- ok = false;
+ if (!fiemap || !fiemap->HasPinnedExtents()) {
+ LOG(ERROR) << "Image is missing or was moved: " << image_path;
+ return false;
}
}
- return ok;
+ return true;
}
bool ImageManager::DisableImage(const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6d0975150..66633916a 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -34,13 +34,10 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
-#include "utility.h"
-
using namespace android::dm;
using namespace std::literals;
using android::base::unique_fd;
using android::fiemap::ImageManager;
-using android::fiemap::IsSubdir;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::PartitionOpener;
using android::fs_mgr::WaitForFile;
@@ -134,51 +131,6 @@ TEST_F(NativeTest, GetMappedImageDevice) {
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
-namespace {
-
-struct IsSubdirTestParam {
- std::string child;
- std::string parent;
- bool result;
-};
-
-class IsSubdirTest : public ::testing::TestWithParam<IsSubdirTestParam> {};
-
-TEST_P(IsSubdirTest, Test) {
- const auto& param = GetParam();
- EXPECT_EQ(param.result, IsSubdir(param.child, param.parent))
- << "IsSubdir(child=\"" << param.child << "\", parent=\"" << param.parent
- << "\") != " << (param.result ? "true" : "false");
-}
-
-std::vector<IsSubdirTestParam> IsSubdirTestValues() {
- // clang-format off
- std::vector<IsSubdirTestParam> base_cases{
- {"/foo/bar", "/foo", true},
- {"/foo/bar/baz", "/foo", true},
- {"/foo", "/foo", true},
- {"/foo", "/", true},
- {"/", "/", true},
- {"/foo", "/foo/bar", false},
- {"/foo", "/bar", false},
- {"/foo-bar", "/foo", false},
- {"/", "/foo", false},
- };
- // clang-format on
- std::vector<IsSubdirTestParam> ret;
- for (const auto& e : base_cases) {
- ret.push_back(e);
- ret.push_back({e.child + "/", e.parent, e.result});
- ret.push_back({e.child, e.parent + "/", e.result});
- ret.push_back({e.child + "/", e.parent + "/", e.result});
- }
- return ret;
-}
-
-INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
-
-} // namespace
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 3c8700025..60b98dce0 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -21,7 +21,6 @@
#include <chrono>
#include <functional>
#include <memory>
-#include <optional>
#include <set>
#include <string>
@@ -38,17 +37,11 @@ class IImageManager {
virtual ~IImageManager() {}
- // Helper for dependency injection.
- struct DeviceInfo {
- std::optional<bool> is_recovery;
- };
-
// When linking to libfiemap_binder, the Open() call will use binder.
// Otherwise, the Open() call will use the ImageManager implementation
- // below. In binder mode, device_info is ignored.
+ // below.
static std::unique_ptr<IImageManager> Open(const std::string& dir_prefix,
- const std::chrono::milliseconds& timeout_ms,
- const DeviceInfo& device_info = {});
+ const std::chrono::milliseconds& timeout_ms);
// Flags for CreateBackingImage().
static constexpr int CREATE_IMAGE_DEFAULT = 0x0;
@@ -138,13 +131,11 @@ class ImageManager final : public IImageManager {
// Return an ImageManager for the given metadata and data directories. Both
// directories must already exist.
static std::unique_ptr<ImageManager> Open(const std::string& metadata_dir,
- const std::string& data_dir,
- const DeviceInfo& device_info = {});
+ const std::string& data_dir);
// Helper function that derives the metadata and data dirs given a single
// prefix.
- static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix,
- const DeviceInfo& device_info = {});
+ static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
// Methods that must be implemented from IImageManager.
FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
@@ -175,8 +166,7 @@ class ImageManager final : public IImageManager {
FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
private:
- ImageManager(const std::string& metadata_dir, const std::string& data_dir,
- const DeviceInfo& device_info);
+ ImageManager(const std::string& metadata_dir, const std::string& data_dir);
std::string GetImageHeaderPath(const std::string& name);
std::string GetStatusFilePath(const std::string& image_name);
bool MapWithLoopDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
@@ -186,8 +176,6 @@ class ImageManager final : public IImageManager {
bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
const std::chrono::milliseconds& timeout_ms, std::string* path);
bool UnmapImageDevice(const std::string& name, bool force);
- bool IsUnreliablePinningAllowed() const;
- bool MetadataDirIsTest() const;
ImageManager(const ImageManager&) = delete;
ImageManager& operator=(const ImageManager&) = delete;
@@ -197,7 +185,6 @@ class ImageManager final : public IImageManager {
std::string metadata_dir_;
std::string data_dir_;
std::unique_ptr<IPartitionOpener> partition_opener_;
- DeviceInfo device_info_;
};
// RAII helper class for mapping and opening devices with an ImageManager.
@@ -209,7 +196,7 @@ class MappedDevice final {
~MappedDevice();
- int fd() const { return fd_.get(); }
+ int fd() const { return fd_; }
const std::string& path() const { return path_; }
protected:
diff --git a/fs_mgr/libfiemap/passthrough.cpp b/fs_mgr/libfiemap/passthrough.cpp
index d521804c5..1ccd9a0c6 100644
--- a/fs_mgr/libfiemap/passthrough.cpp
+++ b/fs_mgr/libfiemap/passthrough.cpp
@@ -20,10 +20,9 @@ namespace android {
namespace fiemap {
std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir_prefix,
- const std::chrono::milliseconds& timeout_ms,
- const DeviceInfo& device_info) {
+ const std::chrono::milliseconds& timeout_ms) {
(void)timeout_ms;
- return ImageManager::Open(dir_prefix, device_info);
+ return ImageManager::Open(dir_prefix);
}
} // namespace fiemap
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 36bb3dfee..12c73979d 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -266,7 +266,7 @@ bool SplitFiemap::Write(const void* data, uint64_t bytes) {
cursor_file_pos_ += bytes_to_write;
}
- // If we've reached the end of the current file, close it.
+ // If we've reached the end of the current file, close it for sanity.
if (cursor_file_pos_ == file->size()) {
cursor_fd_ = {};
}
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index 54cf1836b..bbb0510fa 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -139,7 +139,8 @@ bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
}
*bdev_name = ::android::base::Basename(sysfs_bdev);
- // Check that the symlink doesn't point to itself.
+ // Paranoid sanity check to make sure we just didn't get the
+ // input in return as-is.
if (sysfs_bdev == *bdev_name) {
LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
return false;
@@ -167,30 +168,5 @@ bool FilesystemHasReliablePinning(const std::string& file, bool* supported) {
return F2fsPinBeforeAllocate(fd, supported);
}
-bool IsSubdir(const std::string& child, const std::string& parent) {
- // Precondition: both are absolute paths.
- CHECK(android::base::StartsWith(child, "/")) << "Not an absolute path: " << child;
- CHECK(android::base::StartsWith(parent, "/")) << "Not an absolute path: " << parent;
-
- // Remove extraneous "/" at the end.
- std::string_view child_sv = child;
- while (child_sv != "/" && android::base::ConsumeSuffix(&child_sv, "/"))
- ;
-
- std::string_view parent_sv = parent;
- while (parent_sv != "/" && android::base::ConsumeSuffix(&parent_sv, "/"))
- ;
-
- // IsSubdir(anything, "/") => true
- if (parent_sv == "/") return true;
-
- // IsSubdir("/foo", "/foo") => true
- if (parent_sv == child_sv) return true;
-
- // IsSubdir("/foo/bar", "/foo") => true
- // IsSubdir("/foo-bar", "/foo") => false
- return android::base::StartsWith(child_sv, std::string(parent_sv) + "/");
-}
-
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index aa40f79d1..4c0bc2b1c 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -51,9 +51,5 @@ bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name);
// cases (such as snapshots or adb remount).
bool FilesystemHasReliablePinning(const std::string& file, bool* supported);
-// Crude implementation to check if |child| is a subdir of |parent|.
-// Assume both are absolute paths.
-bool IsSubdir(const std::string& child, const std::string& parent);
-
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 6892025a1..8fb969701 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -14,16 +14,6 @@
// limitations under the License.
//
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_core_fs_mgr_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- default_applicable_licenses: ["system_core_fs_mgr_license"],
-}
-
cc_library_static {
name: "libfs_avb",
defaults: ["fs_mgr_defaults"],
@@ -78,7 +68,6 @@ cc_defaults {
shared_libs: [
"libbase",
"libchrome",
- "libcrypto",
],
target: {
darwin: {
@@ -102,16 +91,11 @@ cc_test_host {
name: "libfs_avb_test",
defaults: ["libfs_avb_host_test_defaults"],
test_suites: ["general-tests"],
- test_options: {
- unit_test: true,
- },
static_libs: [
"libfs_avb_test_util",
],
- compile_multilib: "first",
- data: [
- ":avbtool",
- ":fec",
+ shared_libs: [
+ "libcrypto",
],
srcs: [
"tests/basic_test.cpp",
@@ -124,17 +108,9 @@ cc_test_host {
name: "libfs_avb_internal_test",
defaults: ["libfs_avb_host_test_defaults"],
test_suites: ["general-tests"],
- test_options: {
- unit_test: true,
- },
static_libs: [
"libfs_avb_test_util",
],
- compile_multilib: "first",
- data: [
- ":avbtool",
- ":fec",
- ],
srcs: [
"avb_util.cpp",
"util.cpp",
diff --git a/fs_mgr/libfs_avb/TEST_MAPPING b/fs_mgr/libfs_avb/TEST_MAPPING
new file mode 100644
index 000000000..b0f36d40e
--- /dev/null
+++ b/fs_mgr/libfs_avb/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "libfs_avb_test",
+ "host": true
+ },
+ {
+ "name": "libfs_avb_internal_test",
+ "host": true
+ }
+ ]
+}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 46072bb70..c192bf572 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -52,16 +52,16 @@ static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64
partition, offset, num_bytes, buffer, out_num_read);
}
-static AvbIOResult no_op_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
- size_t rollback_index_location ATTRIBUTE_UNUSED,
- uint64_t* out_rollback_index) {
+static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
+ size_t rollback_index_location ATTRIBUTE_UNUSED,
+ uint64_t* out_rollback_index) {
// rollback_index has been checked in bootloader phase.
// In user-space, returns the smallest value 0 to pass the check.
*out_rollback_index = 0;
return AVB_IO_RESULT_OK;
}
-static AvbIOResult no_op_validate_vbmeta_public_key(
+static AvbIOResult dummy_validate_vbmeta_public_key(
AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
size_t public_key_length ATTRIBUTE_UNUSED,
const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
@@ -76,8 +76,8 @@ static AvbIOResult no_op_validate_vbmeta_public_key(
return AVB_IO_RESULT_OK;
}
-static AvbIOResult no_op_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
- bool* out_is_unlocked) {
+static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
+ bool* out_is_unlocked) {
// The function is for bootloader to update the value into
// androidboot.vbmeta.device_state in kernel cmdline.
// In user-space, returns true as we don't need to update it anymore.
@@ -85,9 +85,9 @@ static AvbIOResult no_op_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
return AVB_IO_RESULT_OK;
}
-static AvbIOResult no_op_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- char* guid_buf, size_t guid_buf_size) {
+static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ char* guid_buf, size_t guid_buf_size) {
// The function is for bootloader to set the correct UUID
// for a given partition in kernel cmdline.
// In user-space, returns a faking one as we don't need to update
@@ -96,9 +96,9 @@ static AvbIOResult no_op_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNU
return AVB_IO_RESULT_OK;
}
-static AvbIOResult no_op_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- uint64_t* out_size_num_byte) {
+static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ uint64_t* out_size_num_byte) {
// The function is for bootloader to load entire content of AVB HASH partitions.
// In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
*out_size_num_byte = 0;
@@ -123,15 +123,15 @@ FsManagerAvbOps::FsManagerAvbOps() {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
- // user-space so we set them as no-op operations. Also zero the entire
+ // user-space so we set them as dummy operations. Also zero the entire
// struct so operations added in the future will be set to NULL.
memset(&avb_ops_, 0, sizeof(AvbOps));
avb_ops_.read_from_partition = read_from_partition;
- avb_ops_.read_rollback_index = no_op_read_rollback_index;
- avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key;
- avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked;
- avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition;
- avb_ops_.get_size_of_partition = no_op_get_size_of_partition;
+ avb_ops_.read_rollback_index = dummy_read_rollback_index;
+ avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
+ avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
+ avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
+ avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 31494c189..228867411 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -61,9 +61,7 @@ bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
// Converts veritymode to the format used in kernel.
std::string dm_verity_mode;
- if (verity_mode == "panicking") {
- dm_verity_mode = "panic_on_corruption";
- } else if (verity_mode == "enforcing") {
+ if (verity_mode == "enforcing") {
dm_verity_mode = "restart_on_corruption";
} else if (verity_mode == "logging") {
dm_verity_mode = "ignore_corruption";
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 1da71176c..5d504ab0d 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -226,7 +226,7 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
return nullptr;
}
- // Validity check here because we have to use vbmeta_images_[0] below.
+ // Sanity check here because we have to use vbmeta_images_[0] below.
if (avb_handle->vbmeta_images_.size() < 1) {
LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded";
return nullptr;
@@ -405,11 +405,11 @@ AvbUniquePtr AvbHandle::Open() {
// - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).
// Might occur in either the top-level vbmeta or a chained vbmeta.
// - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).
- // Could only occur in a chained vbmeta. Because we have *no-op* operations in
+ // Could only occur in a chained vbmeta. Because we have *dummy* operations in
// FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate
// the public key of the top-level vbmeta always pass in userspace here.
//
- // The following verify result won't happen, because the *no-op* operation
+ // The following verify result won't happen, because the *dummy* operation
// avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked
// vbmeta images, which should be caught in the bootloader stage, won't be detected here.
// - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
@@ -433,16 +433,6 @@ AvbUniquePtr AvbHandle::Open() {
// Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
- // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
- std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
- if (!avb_verifier || !avb_verifier->VerifyVbmetaImages(avb_handle->vbmeta_images_)) {
- LERROR << "Failed to verify vbmeta digest";
- if (!allow_verification_error) {
- LERROR << "vbmeta digest error isn't allowed ";
- return nullptr;
- }
- }
-
// Checks whether FLAGS_VERIFICATION_DISABLED is set:
// - Only the top-level vbmeta struct is read.
// - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
@@ -453,16 +443,26 @@ AvbUniquePtr AvbHandle::Open() {
bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
- // Checks whether FLAGS_HASHTREE_DISABLED is set.
- // - vbmeta struct in all partitions are still processed, just disable
- // dm-verity in the user space.
- bool hashtree_disabled =
- ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
-
if (verification_disabled) {
avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
- } else if (hashtree_disabled) {
- avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+ } else {
+ // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+ std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
+ if (!avb_verifier) {
+ LERROR << "Failed to create AvbVerifier";
+ return nullptr;
+ }
+ if (!avb_verifier->VerifyVbmetaImages(avb_handle->vbmeta_images_)) {
+ LERROR << "VerifyVbmetaImages failed";
+ return nullptr;
+ }
+
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+ }
}
LINFO << "Returning avb_handle with status: " << avb_handle->status_;
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 18275665c..784eb9cf9 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -216,11 +216,9 @@ TEST_F(AvbUtilTest, GetAvbFooter) {
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 2304 bytes\n"
- "Public key (sha1): 5227b569de003adc7f8ec3fc03e05dfbd969abad\n"
"Algorithm: SHA512_RSA8192\n"
"Rollback Index: 20\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hashtree descriptor:\n"
@@ -348,11 +346,9 @@ TEST_F(AvbUtilTest, GetVBMetaHeader) {
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 1216 bytes\n"
- "Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
"Algorithm: SHA256_RSA4096\n"
"Rollback Index: 10\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hash descriptor:\n"
@@ -643,11 +639,9 @@ TEST_F(AvbUtilTest, VerifyVBMetaDataWithoutFooter) {
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 3840 bytes\n"
- "Public key (sha1): 5227b569de003adc7f8ec3fc03e05dfbd969abad\n"
"Algorithm: SHA256_RSA8192\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
@@ -860,11 +854,9 @@ TEST_F(AvbUtilTest, GetChainPartitionInfo) {
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 3840 bytes\n"
- "Public key (sha1): 5227b569de003adc7f8ec3fc03e05dfbd969abad\n"
"Algorithm: SHA256_RSA8192\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
@@ -894,11 +886,9 @@ TEST_F(AvbUtilTest, GetChainPartitionInfo) {
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 2176 bytes\n"
- "Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
"Algorithm: SHA256_RSA4096\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
@@ -946,11 +936,9 @@ TEST_F(AvbUtilTest, GetChainPartitionInfoNone) {
"Header Block: 256 bytes\n"
"Authentication Block: 320 bytes\n"
"Auxiliary Block: 960 bytes\n"
- "Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hash descriptor:\n"
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
index 1c47c0758..5a1cd0dd8 100644
--- a/fs_mgr/libfs_avb/tests/basic_test.cpp
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -59,11 +59,9 @@ TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
"Header Block: 256 bytes\n"
"Authentication Block: 320 bytes\n"
"Auxiliary Block: 576 bytes\n"
- "Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" (none)\n",
@@ -91,11 +89,9 @@ TEST_F(BaseFsAvbTest, AddHashFooter) {
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 1216 bytes\n"
- "Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
"Algorithm: SHA256_RSA4096\n"
"Rollback Index: 10\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hash descriptor:\n"
@@ -130,11 +126,9 @@ TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 2304 bytes\n"
- "Public key (sha1): 5227b569de003adc7f8ec3fc03e05dfbd969abad\n"
"Algorithm: SHA512_RSA8192\n"
"Rollback Index: 20\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hashtree descriptor:\n"
@@ -186,11 +180,9 @@ TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
"Header Block: 256 bytes\n"
"Authentication Block: 320 bytes\n"
"Auxiliary Block: 960 bytes\n"
- "Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hash descriptor:\n"
@@ -257,11 +249,9 @@ TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
"Header Block: 256 bytes\n"
"Authentication Block: 1088 bytes\n"
"Auxiliary Block: 3840 bytes\n"
- "Public key (sha1): 5227b569de003adc7f8ec3fc03e05dfbd969abad\n"
"Algorithm: SHA256_RSA8192\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Chain Partition descriptor:\n"
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
index 1c95cf0fe..17f4c4e11 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -122,7 +122,6 @@ base::FilePath BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_pat
const size_t padding_size) {
VBMetaImage vbmeta_image;
vbmeta_image.path = test_dir_.Append(output_file_name);
- GTEST_LOG_(INFO) << "ExtractVBMetaImage: " << image_path << " to " << output_file_name;
EXPECT_COMMAND(0,
"avbtool extract_vbmeta_image"
" --image %s"
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
index 5ec1e90eb..7c340099e 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
@@ -57,11 +57,9 @@ TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor) {
"Header Block: 256 bytes\n"
"Authentication Block: 576 bytes\n"
"Auxiliary Block: 1280 bytes\n"
- "Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n"
"Algorithm: SHA512_RSA4096\n"
"Rollback Index: 20\n"
"Flags: 0\n"
- "Rollback Index Location: 0\n"
"Release String: 'unit test'\n"
"Descriptors:\n"
" Hashtree descriptor:\n"
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index a52a00d27..5c388aaeb 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -222,7 +222,7 @@ TEST(BasicUtilTest, ListFiles) {
base::FilePath test_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
- // Generates test files to list.
+ // Generates dummy files to list.
base::FilePath file_path_1 = test_dir.Append("1.txt");
ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
base::FilePath file_path_2 = test_dir.Append("2.txt");
@@ -253,7 +253,7 @@ TEST(BasicUtilTest, ListFilesShouldDiscardSymlink) {
base::FilePath test_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
- // Generates test files to list.
+ // Generates dummy files to list.
base::FilePath file_path_1 = test_dir.Append("1.txt");
ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
base::FilePath file_path_2 = test_dir.Append("2.txt");
@@ -281,7 +281,7 @@ TEST(BasicUtilTest, ListFilesOpenDirFailure) {
base::FilePath tmp_dir;
ASSERT_TRUE(GetTempDir(&tmp_dir));
- // Generates test files to list.
+ // Generates dummy files to list.
base::FilePath no_such_dir = tmp_dir.Append("not_such_dir");
auto fail = ListFiles(no_such_dir.value());
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 7e528b164..a779a7822 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
liblp_lib_deps = [
"libbase",
"liblog",
@@ -111,3 +107,7 @@ cc_test {
name: "vts_kernel_liblp_test",
defaults: ["liblp_test_defaults"],
}
+
+vts_config {
+ name: "VtsKernelLiblpTest",
+}
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
new file mode 100644
index 000000000..2eb0ad154
--- /dev/null
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelLiblpTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsKernelLiblpTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ <option name="precondition-first-api-level" value="29" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 6cb2c5172..5554f7794 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,7 +19,6 @@
#include <string.h>
#include <algorithm>
-#include <limits>
#include <android-base/unique_fd.h>
@@ -31,22 +30,6 @@
namespace android {
namespace fs_mgr {
-std::ostream& operator<<(std::ostream& os, const Extent& extent) {
- switch (extent.GetExtentType()) {
- case ExtentType::kZero: {
- os << "type: Zero";
- break;
- }
- case ExtentType::kLinear: {
- auto linear_extent = static_cast<const LinearExtent*>(&extent);
- os << "type: Linear, physical sectors: " << linear_extent->physical_sector()
- << ", end sectors: " << linear_extent->end_sector();
- break;
- }
- }
- return os;
-}
-
bool LinearExtent::AddTo(LpMetadata* out) const {
if (device_index_ >= out->block_devices.size()) {
LERROR << "Extent references unknown block device.";
@@ -57,17 +40,6 @@ bool LinearExtent::AddTo(LpMetadata* out) const {
return true;
}
-bool LinearExtent::operator==(const android::fs_mgr::Extent& other) const {
- if (other.GetExtentType() != ExtentType::kLinear) {
- return false;
- }
-
- auto other_ptr = static_cast<const LinearExtent*>(&other);
- return num_sectors_ == other_ptr->num_sectors_ &&
- physical_sector_ == other_ptr->physical_sector_ &&
- device_index_ == other_ptr->device_index_;
-}
-
bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
if (device_index_ != other.device_index()) {
return false;
@@ -91,10 +63,6 @@ bool ZeroExtent::AddTo(LpMetadata* out) const {
return true;
}
-bool ZeroExtent::operator==(const android::fs_mgr::Extent& other) const {
- return other.GetExtentType() == ExtentType::kZero && num_sectors_ == other.num_sectors();
-}
-
Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
: name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
@@ -383,6 +351,11 @@ static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
<< " partition alignment is not sector-aligned.";
return false;
}
+ if (device_info.alignment_offset > device_info.alignment) {
+ LERROR << "Block device " << device_info.partition_name
+ << " partition alignment offset is greater than its alignment.";
+ return false;
+ }
return true;
}
@@ -403,10 +376,7 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
}
// Align the metadata size up to the nearest sector.
- if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
- LERROR << "Max metadata size " << metadata_max_size << " is too large.";
- return false;
- }
+ metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
// Validate and build the block device list.
uint32_t logical_block_size = 0;
@@ -438,15 +408,10 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
// untouched to be compatible code that looks for an MBR. Thus we
// start counting free sectors at sector 1, not 0.
uint64_t free_area_start = LP_SECTOR_SIZE;
- bool ok;
- if (out.alignment) {
- ok = AlignTo(free_area_start, out.alignment, &free_area_start);
+ if (out.alignment || out.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
} else {
- ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
- }
- if (!ok) {
- LERROR << "Integer overflow computing free area start";
- return false;
+ free_area_start = AlignTo(free_area_start, logical_block_size);
}
out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -483,15 +448,10 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
- bool ok;
- if (super.alignment) {
- ok = AlignTo(free_area_start, super.alignment, &free_area_start);
+ if (super.alignment || super.alignment_offset) {
+ free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
} else {
- ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
- }
- if (!ok) {
- LERROR << "Integer overflow computing free area start";
- return false;
+ free_area_start = AlignTo(free_area_start, logical_block_size);
}
super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -544,7 +504,7 @@ Partition* MetadataBuilder::AddPartition(std::string_view name, std::string_view
return partitions_.back().get();
}
-Partition* MetadataBuilder::FindPartition(std::string_view name) const {
+Partition* MetadataBuilder::FindPartition(std::string_view name) {
for (const auto& partition : partitions_) {
if (partition->name() == name) {
return partition.get();
@@ -553,7 +513,7 @@ Partition* MetadataBuilder::FindPartition(std::string_view name) const {
return nullptr;
}
-PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) const {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
for (const auto& group : groups_) {
if (group->name() == group_name) {
return group.get();
@@ -591,11 +551,7 @@ void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,
const Interval& current = extents[i];
DCHECK(previous.device_index == current.device_index);
- uint64_t aligned;
- if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
- LERROR << "Sector " << previous.end << " caused integer overflow.";
- continue;
- }
+ uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -781,10 +737,7 @@ std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
// Choose an aligned sector for the midpoint. This could lead to one half
// being slightly larger than the other, but this will not restrict the
// size of partitions (it might lead to one extra extent if "B" overflows).
- if (!AlignSector(super, midpoint, &midpoint)) {
- LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
- return free_list;
- }
+ midpoint = AlignSector(super, midpoint);
std::vector<Interval> first_half;
std::vector<Interval> second_half;
@@ -822,11 +775,7 @@ std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
// If the sector ends where the next aligned chunk begins, then there's
// no missing gap to try and allocate.
const auto& block_device = block_devices_[extent->device_index()];
- uint64_t next_aligned_sector;
- if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
- LERROR << "Integer overflow aligning sector " << extent->end_sector();
- return nullptr;
- }
+ uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
if (extent->end_sector() == next_aligned_sector) {
return nullptr;
}
@@ -983,19 +932,13 @@ uint64_t MetadataBuilder::UsedSpace() const {
return size;
}
-bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
- uint64_t* out) const {
+uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
+ uint64_t sector) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- if (!AlignTo(lba, block_device.alignment, out)) {
- return false;
- }
- if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
- return false;
- }
- *out /= LP_SECTOR_SIZE;
- return true;
+ uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
+ return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
}
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
@@ -1069,12 +1012,7 @@ bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo&
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
const std::vector<Interval>& free_region_hint) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size;
- if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
- LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
- << " bytes; integer overflow.";
- return false;
- }
+ uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
uint64_t old_size = partition->size();
if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
@@ -1296,50 +1234,5 @@ uint64_t MetadataBuilder::logical_block_size() const {
return geometry_.logical_block_size;
}
-bool MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
- const MetadataBuilder& source_metadata, uint32_t source_slot_number,
- const MetadataBuilder& target_metadata, uint32_t target_slot_number,
- const std::vector<std::string>& partitions) {
- for (const auto& base_name : partitions) {
- // Find the partition in metadata with the slot suffix.
- auto target_partition_name = base_name + SlotSuffixForSlotNumber(target_slot_number);
- const auto target_partition = target_metadata.FindPartition(target_partition_name);
- if (!target_partition) {
- LERROR << "Failed to find partition " << target_partition_name << " in metadata slot "
- << target_slot_number;
- return false;
- }
-
- auto source_partition_name = base_name + SlotSuffixForSlotNumber(source_slot_number);
- const auto source_partition = source_metadata.FindPartition(source_partition_name);
- if (!source_partition) {
- LERROR << "Failed to find partition " << source_partition << " in metadata slot "
- << source_slot_number;
- return false;
- }
-
- // We expect the partitions in the target metadata to have the identical extents as the
- // one in the source metadata. Because they are copied in NewForUpdate.
- if (target_partition->extents().size() != source_partition->extents().size()) {
- LERROR << "Extents count mismatch for partition " << base_name << " target slot has "
- << target_partition->extents().size() << ", source slot has "
- << source_partition->extents().size();
- return false;
- }
-
- for (size_t i = 0; i < target_partition->extents().size(); i++) {
- const auto& src_extent = *source_partition->extents()[i];
- const auto& tgt_extent = *target_partition->extents()[i];
- if (tgt_extent != src_extent) {
- LERROR << "Extents " << i << " is different for partition " << base_name;
- LERROR << "tgt extent " << tgt_extent << "; src extent " << src_extent;
- return false;
- }
- }
- }
-
- return true;
-}
-
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 72827eb6c..977ebe3c0 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -174,12 +174,12 @@ TEST_F(BuilderTest, InternalAlignment) {
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 1536);
+ EXPECT_EQ(super_device->first_logical_sector, 1472);
- // Alignment offset without alignment is ignored.
+ // Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
builder = MetadataBuilder::New(device_info, 1024, 2);
- ASSERT_NE(builder, nullptr);
+ ASSERT_EQ(builder, nullptr);
// Test a small alignment with an alignment offset.
device_info.alignment = 12 * 1024;
@@ -190,7 +190,7 @@ TEST_F(BuilderTest, InternalAlignment) {
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 168);
+ EXPECT_EQ(super_device->first_logical_sector, 174);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@@ -200,7 +200,7 @@ TEST_F(BuilderTest, InternalAlignment) {
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 154);
+ EXPECT_EQ(super_device->first_logical_sector, 160);
}
TEST_F(BuilderTest, InternalPartitionAlignment) {
@@ -228,14 +228,13 @@ TEST_F(BuilderTest, InternalPartitionAlignment) {
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(extent.num_sectors, 80);
- uint64_t aligned_lba;
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
- ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
+ uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
EXPECT_EQ(lba, aligned_lba);
}
- // Check one extent.
- EXPECT_EQ(exported->extents.back().target_data, 3072);
+ // Sanity check one extent.
+ EXPECT_EQ(exported->extents.back().target_data, 3008);
}
TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -444,6 +443,11 @@ TEST_F(BuilderTest, MetadataTooLarge) {
device_info.alignment = 131072;
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
EXPECT_EQ(builder, nullptr);
+
+ device_info.alignment = 0;
+ device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
+ builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
}
TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
@@ -648,7 +652,7 @@ TEST_F(BuilderTest, MultipleBlockDevices) {
};
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
ASSERT_NE(builder, nullptr);
- EXPECT_EQ(builder->AllocatableSpace(), 467402752);
+ EXPECT_EQ(builder->AllocatableSpace(), 467238912);
// Create a partition that spans 3 devices.
Partition* p = builder->AddPartition("system_a", 0);
@@ -671,17 +675,17 @@ TEST_F(BuilderTest, MultipleBlockDevices) {
EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
ASSERT_EQ(metadata->extents.size(), 3);
- EXPECT_EQ(metadata->extents[0].num_sectors, 522752);
+ EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[0].target_data, 1536);
+ EXPECT_EQ(metadata->extents[0].target_data, 1984);
EXPECT_EQ(metadata->extents[0].target_source, 0);
- EXPECT_EQ(metadata->extents[1].num_sectors, 260608);
+ EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[1].target_data, 1536);
+ EXPECT_EQ(metadata->extents[1].target_data, 1472);
EXPECT_EQ(metadata->extents[1].target_source, 1);
- EXPECT_EQ(metadata->extents[2].num_sectors, 128704);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[2].target_data, 1536);
+ EXPECT_EQ(metadata->extents[2].target_data, 1472);
EXPECT_EQ(metadata->extents[2].target_source, 2);
}
@@ -942,10 +946,9 @@ static Interval ToInterval(const std::unique_ptr<Extent>& extent) {
}
static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
- const std::string& partition_name, const std::string& group_name,
- uint64_t num_sectors, uint64_t start_sector,
- std::vector<Interval>* intervals = nullptr) {
- Partition* p = builder->AddPartition(partition_name, group_name, 0);
+ const std::string& partition_name, uint64_t num_sectors,
+ uint64_t start_sector, std::vector<Interval>* intervals) {
+ Partition* p = builder->AddPartition(partition_name, "group", 0);
ASSERT_NE(p, nullptr);
ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
ASSERT_EQ(p->extents().size(), 1);
@@ -973,17 +976,17 @@ TEST_F(BuilderTest, CollidedExtents) {
ASSERT_TRUE(builder->AddGroup("group", 0));
std::vector<Interval> old_intervals;
- AddPartition(builder, "system", "group", 10229008, 2048, &old_intervals);
- AddPartition(builder, "test_a", "group", 648, 12709888, &old_intervals);
- AddPartition(builder, "test_b", "group", 625184, 12711936, &old_intervals);
- AddPartition(builder, "test_c", "group", 130912, 13338624, &old_intervals);
- AddPartition(builder, "test_d", "group", 888, 13469696, &old_intervals);
- AddPartition(builder, "test_e", "group", 888, 13471744, &old_intervals);
- AddPartition(builder, "test_f", "group", 888, 13475840, &old_intervals);
- AddPartition(builder, "test_g", "group", 888, 13477888, &old_intervals);
+ AddPartition(builder, "system", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
// Don't track the first vendor interval, since it will get extended.
- AddPartition(builder, "vendor", "group", 2477920, 10231808, nullptr);
+ AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
std::vector<Interval> new_intervals;
@@ -1016,76 +1019,3 @@ TEST_F(BuilderTest, LinearExtentOverlap) {
EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
}
-
-TEST_F(BuilderTest, AlignFreeRegion) {
- BlockDeviceInfo super("super", 8_GiB, 786432, 0, 4096);
- std::vector<BlockDeviceInfo> block_devices = {super};
-
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
- ASSERT_NE(builder, nullptr);
-
- Partition* p = builder->AddPartition("system", "default", 0);
- ASSERT_NE(p, nullptr);
- ASSERT_TRUE(builder->AddLinearExtent(p, "super", 64, (super.alignment + 4096) / 512));
-
- p = builder->AddPartition("vendor", "default", 0);
- ASSERT_NE(p, nullptr);
- ASSERT_TRUE(builder->ResizePartition(p, 2_GiB));
-
- const auto& extents = p->extents();
- ASSERT_EQ(extents.size(), 2);
-
- LinearExtent* e1 = extents[0]->AsLinearExtent();
- ASSERT_NE(e1, nullptr);
- LinearExtent* e2 = extents[1]->AsLinearExtent();
- ASSERT_NE(e2, nullptr);
-
- // The misaligned partition starting at sector 1544 should not cause any
- // overlap with previous extents. We should see vendor punch a hole where
- // "system" is, extending the hole up to the next aligned block.
- EXPECT_EQ(e1->physical_sector(), 1536);
- EXPECT_EQ(e1->end_sector(), 1544);
- EXPECT_EQ(e2->physical_sector(), 3072);
- EXPECT_EQ(e2->end_sector(), 4197368);
-}
-
-TEST_F(BuilderTest, ResizeOverflow) {
- BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
- std::vector<BlockDeviceInfo> block_devices = {super};
-
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
- ASSERT_NE(builder, nullptr);
-
- ASSERT_TRUE(builder->AddGroup("group", 0));
-
- Partition* p = builder->AddPartition("system", "default", 0);
- ASSERT_NE(p, nullptr);
- ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
-}
-
-TEST_F(BuilderTest, VerifyExtent) {
- auto source_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
- ASSERT_NE(source_builder, nullptr);
- ASSERT_TRUE(source_builder->AddGroup("test_group_a", 40960));
- ASSERT_TRUE(source_builder->AddGroup("test_group_b", 40960));
- AddPartition(source_builder, "system_a", "test_group_a", 8192, 2048);
- AddPartition(source_builder, "vendor_a", "test_group_a", 10240, 10240);
- AddPartition(source_builder, "system_b", "test_group_b", 8192, 20480);
-
- auto target_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
- ASSERT_NE(target_builder, nullptr);
- ASSERT_TRUE(target_builder->AddGroup("test_group_b", 40960));
- AddPartition(target_builder, "system_b", "test_group_b", 8192, 2048);
- AddPartition(target_builder, "vendor_b", "test_group_b", 10240, 10240);
-
- ASSERT_TRUE(MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
- *source_builder, 0, *target_builder, 1, std::vector<std::string>{"system", "vendor"}));
-
- target_builder->RemovePartition("vendor_b");
- ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
- *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
-
- AddPartition(target_builder, "vendor_b", "test_group_b", 1000, 10240);
- ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
- *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
-}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 236fd8dd3..382d53d52 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -47,10 +47,15 @@ TEST_F(DeviceTest, BlockDeviceInfo) {
BlockDeviceInfo device_info;
ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
- // Check that the device doesn't give us some weird inefficient
+ // Sanity check that the device doesn't give us some weird inefficient
// alignment.
EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+ EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+ EXPECT_LE(device_info.alignment_offset, INT_MAX);
EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
+
+ // Having an alignment offset > alignment doesn't really make sense.
+ EXPECT_LT(device_info.alignment_offset, device_info.alignment);
}
TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 0b1e522d6..e4d92ca90 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -124,7 +124,7 @@ bool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {
}
bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
- unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return false;
@@ -184,7 +184,7 @@ bool ImageBuilder::IsValid() const {
}
bool ImageBuilder::Export(const std::string& file) {
- unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
return false;
@@ -208,7 +208,7 @@ bool ImageBuilder::ExportFiles(const std::string& output_dir) {
std::string file_name = "super_" + name + ".img";
std::string file_path = output_dir + "/" + file_name;
- static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+ static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW;
unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
if (fd < 0) {
PERROR << "open failed: " << file_path;
@@ -443,7 +443,7 @@ bool ImageBuilder::CheckExtentOrdering() {
}
int ImageBuilder::OpenImageFile(const std::string& file) {
- unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY);
+ android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
if (source_fd < 0) {
PERROR << "open image file failed: " << file;
return -1;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 54f31bc36..6c7a70728 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -42,11 +42,6 @@ static constexpr uint32_t kDefaultBlockSize = 4096;
// Name of the default group in a metadata.
static constexpr std::string_view kDefaultGroup = "default";
-enum class ExtentType {
- kZero,
- kLinear,
-};
-
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
@@ -55,10 +50,6 @@ class Extent {
virtual bool AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
- virtual ExtentType GetExtentType() const = 0;
-
- virtual bool operator==(const Extent& other) const = 0;
- virtual bool operator!=(const Extent& other) const { return !(*this == other); }
uint64_t num_sectors() const { return num_sectors_; }
void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
@@ -67,8 +58,6 @@ class Extent {
uint64_t num_sectors_;
};
-std::ostream& operator<<(std::ostream& os, const Extent& extent);
-
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
@@ -77,9 +66,6 @@ class LinearExtent final : public Extent {
bool AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
- ExtentType GetExtentType() const override { return ExtentType::kLinear; }
-
- bool operator==(const Extent& other) const override;
uint64_t physical_sector() const { return physical_sector_; }
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
@@ -101,9 +87,6 @@ class ZeroExtent final : public Extent {
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
bool AddTo(LpMetadata* out) const override;
- ExtentType GetExtentType() const override { return ExtentType::kZero; }
-
- bool operator==(const Extent& other) const override;
};
class PartitionGroup final {
@@ -161,6 +144,7 @@ class Partition final {
std::vector<std::unique_ptr<Extent>> extents_;
uint32_t attributes_;
uint64_t size_;
+ bool disabled_;
};
// An interval in the metadata. This is similar to a LinearExtent with one difference.
@@ -260,14 +244,6 @@ class MetadataBuilder {
return New(device_info, metadata_max_size, metadata_slot_count);
}
- // Verifies that the given partitions in the metadata have the same extents as the source
- // metadata.
- static bool VerifyExtentsAgainstSourceMetadata(const MetadataBuilder& source_metadata,
- uint32_t source_slot_number,
- const MetadataBuilder& target_metadata,
- uint32_t target_slot_number,
- const std::vector<std::string>& partitions);
-
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@@ -292,10 +268,10 @@ class MetadataBuilder {
void RemovePartition(std::string_view name);
// Find a partition by name. If no partition is found, nullptr is returned.
- Partition* FindPartition(std::string_view name) const;
+ Partition* FindPartition(std::string_view name);
// Find a group by name. If no group is found, nullptr is returned.
- PartitionGroup* FindGroup(std::string_view name) const;
+ PartitionGroup* FindGroup(std::string_view name);
// Add a predetermined extent to a partition.
bool AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -347,6 +323,9 @@ class MetadataBuilder {
// Set the LP_HEADER_FLAG_VIRTUAL_AB_DEVICE flag.
void SetVirtualABDeviceFlag();
+ // If set, checks for slot suffixes will be ignored internally.
+ void IgnoreSlotSuffixing();
+
bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
@@ -382,7 +361,7 @@ class MetadataBuilder {
bool GrowPartition(Partition* partition, uint64_t aligned_size,
const std::vector<Interval>& free_region_hint);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;
+ uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 4696ff173..1d4db8525 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -38,9 +38,6 @@ using android::base::unique_fd;
namespace {
std::string GetPartitionAbsolutePath(const std::string& path) {
-#if !defined(__ANDROID__)
- return path;
-#else
if (android::base::StartsWith(path, "/")) {
return path;
}
@@ -52,14 +49,13 @@ std::string GetPartitionAbsolutePath(const std::string& path) {
// Dynamic System Update is installed to an sdcard, which won't be in
// the boot device list.
//
- // mmcblk* is allowed because most devices in /dev/block are not valid for
+ // We whitelist because most devices in /dev/block are not valid for
// storing fiemaps.
if (android::base::StartsWith(path, "mmcblk")) {
return "/dev/block/" + path;
}
}
return by_name;
-#endif
}
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 24ccc0f9d..e6fd9f72a 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -174,7 +174,7 @@ static bool ReadMetadataHeader(Reader* reader, LpMetadata* metadata) {
return false;
}
- // Do basic validity checks before computing the checksum.
+ // Do basic sanity checks before computing the checksum.
if (header.magic != LP_METADATA_HEADER_MAGIC) {
LERROR << "Logical partition metadata has invalid magic value.";
return false;
@@ -255,7 +255,7 @@ static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geome
LpMetadataHeader& header = metadata->header;
- // Check the table size.
+ // Sanity check the table size.
if (header.tables_size > geometry.metadata_max_size) {
LERROR << "Invalid partition metadata header table size.";
return nullptr;
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index aa3a6a0a7..1d86edd4e 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,7 +21,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <limits>
#include <string>
#include <string_view>
@@ -67,26 +66,27 @@ int64_t SeekFile64(int fd, int64_t offset, int whence);
void SHA256(const void* data, size_t length, uint8_t out[32]);
// Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two. Return false on overflow.
-template <typename T>
-bool AlignTo(T base, uint32_t alignment, T* out) {
- static_assert(std::numeric_limits<T>::is_integer);
- static_assert(!std::numeric_limits<T>::is_signed);
+// have to be a power of two.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
if (!alignment) {
- *out = base;
- return true;
+ return base;
}
- T remainder = base % alignment;
+ uint64_t remainder = base % alignment;
if (remainder == 0) {
- *out = base;
- return true;
+ return base;
}
- T to_add = alignment - remainder;
- if (to_add > std::numeric_limits<T>::max() - base) {
- return false;
+ return base + (alignment - remainder);
+}
+
+// Same as the above |AlignTo|, except that |base| is only aligned when added to
+// |alignment_offset|.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
+ uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
+ if (aligned - alignment >= base) {
+ // We overaligned (base < alignment_offset).
+ return aligned - alignment;
}
- *out = base + to_add;
- return true;
+ return aligned;
}
// Update names from C++ strings.
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index fc90872a5..cac3989e2 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <optional>
-
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -60,28 +58,15 @@ TEST(liblp, GetMetadataOffset) {
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
}
-std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {
- uint64_t r;
- if (!AlignTo(base, alignment, &r)) {
- return {};
- }
- return {r};
-}
-
TEST(liblp, AlignTo) {
- EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37));
- EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));
- EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));
- EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));
- EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));
- EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));
- EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));
- EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));
-
- auto u32limit = std::numeric_limits<uint32_t>::max();
- auto u64limit = std::numeric_limits<uint64_t>::max();
- EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});
- EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});
+ EXPECT_EQ(AlignTo(37, 0), 37);
+ EXPECT_EQ(AlignTo(1024, 1024), 1024);
+ EXPECT_EQ(AlignTo(555, 1024), 1024);
+ EXPECT_EQ(AlignTo(555, 1000), 1000);
+ EXPECT_EQ(AlignTo(0, 1024), 0);
+ EXPECT_EQ(AlignTo(54, 32, 30), 62);
+ EXPECT_EQ(AlignTo(32, 32, 30), 62);
+ EXPECT_EQ(AlignTo(17, 32, 30), 30);
}
TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 2708efa15..8bf1ee923 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -81,8 +81,8 @@ std::string SerializeMetadata(const LpMetadata& input) {
return header_blob + tables;
}
-// Perform checks so we don't accidentally overwrite valid metadata with
-// potentially invalid metadata, or random partition data with metadata.
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
const LpMetadata& metadata, const std::string& slot_suffix,
std::string* blob) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6a764e4fa..c11878806 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "libsnapshot_defaults",
defaults: ["fs_mgr_defaults"],
@@ -28,30 +24,21 @@ cc_defaults {
],
shared_libs: [
"libbase",
- "libchrome",
"libcutils",
"liblog",
],
static_libs: [
- "libbrotli",
"libdm",
"libfstab",
"update_metadata-protos",
],
whole_static_libs: [
- "libbrotli",
- "libcutils",
"libext2_uuid",
"libext4_utils",
"libfstab",
- "libsnapshot_cow",
- "libsnapshot_snapuserd",
- "libz",
],
header_libs: [
"libfiemap_headers",
- "libstorage_literals_headers",
- "libupdate_engine_headers",
],
export_static_lib_headers: [
"update_metadata-protos",
@@ -84,11 +71,8 @@ filegroup {
"android/snapshot/snapshot.proto",
"device_info.cpp",
"snapshot.cpp",
- "snapshot_reader.cpp",
"snapshot_stats.cpp",
- "snapshot_stub.cpp",
"snapshot_metadata_updater.cpp",
- "snapshot_writer.cpp",
"partition_cow_creator.cpp",
"return.cpp",
"utility.cpp",
@@ -115,13 +99,9 @@ cc_library_static {
cc_library_static {
name: "libsnapshot_init",
- native_coverage : true,
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
- cflags: [
- "-DLIBSNAPSHOT_NO_COW_WRITE",
- ],
static_libs: [
"libfs_mgr",
],
@@ -135,84 +115,11 @@ cc_library_static {
],
srcs: [":libsnapshot_sources"],
recovery_available: true,
- cflags: [
- "-DLIBSNAPSHOT_NO_COW_WRITE",
- ],
static_libs: [
"libfs_mgr",
],
}
-cc_defaults {
- name: "libsnapshot_cow_defaults",
- defaults: [
- "fs_mgr_defaults",
- ],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- export_include_dirs: ["include"],
- srcs: [
- "cow_decompress.cpp",
- "cow_reader.cpp",
- "cow_writer.cpp",
- "cow_format.cpp",
- ],
-}
-
-cc_library_static {
- name: "libsnapshot_cow",
- defaults: [
- "libsnapshot_cow_defaults",
- ],
- host_supported: true,
- recovery_available: true,
- shared_libs: [
- "libbase",
- "liblog",
- ],
- static_libs: [
- "libbrotli",
- "libz",
- ],
- ramdisk_available: true,
- vendor_ramdisk_available: true,
-}
-
-cc_defaults {
- name: "libsnapshot_snapuserd_defaults",
- defaults: [
- "fs_mgr_defaults",
- ],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- export_include_dirs: ["include"],
- srcs: [
- "snapuserd_client.cpp",
- ],
-}
-
-cc_library_static {
- name: "libsnapshot_snapuserd",
- defaults: [
- "libsnapshot_snapuserd_defaults",
- ],
- recovery_available: true,
- static_libs: [
- "libcutils_sockets",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- ramdisk_available: true,
-}
-
cc_library_static {
name: "libsnapshot_test_helpers",
defaults: ["libsnapshot_defaults"],
@@ -249,9 +156,7 @@ cc_defaults {
srcs: [
"partition_cow_creator_test.cpp",
"snapshot_metadata_updater_test.cpp",
- "snapshot_reader_test.cpp",
"snapshot_test.cpp",
- "snapshot_writer_test.cpp",
],
shared_libs: [
"libbinder",
@@ -264,14 +169,11 @@ cc_defaults {
static_libs: [
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
- "libbrotli",
- "libc++fs",
- "libfs_mgr_binder",
+ "libfs_mgr",
"libgsi",
"libgmock",
"liblp",
"libsnapshot",
- "libsnapshot_cow",
"libsnapshot_test_helpers",
"libsparse",
],
@@ -292,19 +194,20 @@ cc_test {
defaults: ["libsnapshot_test_defaults"],
}
+// For VTS 10
+vts_config {
+ name: "VtsLibsnapshotTest",
+ test_config: "VtsLibsnapshotTest.xml"
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
"snapshotctl.cpp",
],
static_libs: [
- "libbrotli",
- "libc++fs",
"libfstab",
"libsnapshot",
- "libsnapshot_cow",
- "libz",
- "update_metadata-protos",
],
shared_libs: [
"android.hardware.boot@1.0",
@@ -322,297 +225,3 @@ cc_binary {
"libutils",
],
}
-
-cc_test {
- name: "snapshot_power_test",
- srcs: [
- "power_test.cpp",
- ],
- static_libs: [
- "libc++fs",
- "libsnapshot",
- "update_metadata-protos",
- ],
- shared_libs: [
- "libbase",
- "libfs_mgr_binder",
- "liblog",
- ],
- gtest: false,
-}
-
-cc_defaults {
- name: "libsnapshot_fuzzer_defaults",
- native_coverage : true,
- srcs: [
- // Compile the protobuf definition again with type full.
- "android/snapshot/snapshot_fuzz.proto",
- "update_engine/update_metadata.proto",
- "fuzz_utils.cpp",
- "snapshot_fuzz.cpp",
- "snapshot_fuzz_utils.cpp",
-
- // Compile libsnapshot sources directly to avoid dependency
- // to update_metadata-protos
- ":libsnapshot_sources",
- ],
- static_libs: [
- "libbase",
- "libbrotli",
- "libc++fs",
- "libchrome",
- "libcrypto_static",
- "libcutils",
- "libext2_uuid",
- "libext4_utils",
- "libfstab",
- "libfs_mgr",
- "libgtest", // from libsnapshot_test_helpers
- "libgmock", // from libsnapshot_test_helpers
- "liblog",
- "liblp",
- "libsnapshot_cow",
- "libsnapshot_test_helpers",
- "libprotobuf-mutator",
- "libz",
- ],
- header_libs: [
- "libfiemap_headers",
- "libstorage_literals_headers",
- "libupdate_engine_headers",
- ],
- proto: {
- type: "full",
- canonical_path_from_root: false,
- local_include_dirs: ["."],
- },
-}
-
-cc_fuzz {
- name: "libsnapshot_fuzzer",
- defaults: ["libsnapshot_fuzzer_defaults"],
- corpus: ["corpus/*"],
- fuzz_config: {
- cc: ["android-virtual-ab+bugs@google.com"],
- componentid: 30545,
- hotlists: ["1646452"],
- fuzz_on_haiku_host: false,
- fuzz_on_haiku_device: true,
- },
-}
-
-cc_test {
- name: "libsnapshot_fuzzer_test",
- defaults: ["libsnapshot_fuzzer_defaults"],
- data: ["corpus/*"],
- test_suites: [
- "device-tests",
- ],
- auto_gen_config: true,
- require_root: true,
-}
-
-cc_defaults {
- name: "snapuserd_defaults",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "snapuserd_server.cpp",
- "snapuserd.cpp",
- "snapuserd_daemon.cpp",
- "snapuserd_worker.cpp",
- "snapuserd_readahead.cpp",
- ],
-
- cflags: [
- "-Wall",
- "-Werror"
- ],
-
- static_libs: [
- "libbase",
- "libbrotli",
- "libcutils_sockets",
- "libdm",
- "libgflags",
- "liblog",
- "libsnapshot_cow",
- "libz",
- ],
-}
-
-cc_binary {
- name: "snapuserd",
- defaults: ["snapuserd_defaults"],
- init_rc: [
- "snapuserd.rc",
- ],
- static_executable: true,
- system_shared_libs: [],
- ramdisk_available: true,
- vendor_ramdisk_available: true,
- recovery_available: true,
-}
-
-cc_test {
- name: "cow_api_test",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "cow_api_test.cpp",
- ],
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "libbase",
- "libcrypto",
- "liblog",
- "libz",
- ],
- static_libs: [
- "libbrotli",
- "libgtest",
- "libsnapshot_cow",
- ],
- test_suites: [
- "device-tests"
- ],
- test_min_api_level: 30,
- auto_gen_config: true,
- require_root: false,
- host_supported: true,
-}
-
-cc_binary {
- name: "make_cow_from_ab_ota",
- host_supported: true,
- device_supported: false,
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- static_libs: [
- "libbase",
- "libbspatch",
- "libbrotli",
- "libbz",
- "libchrome",
- "libcrypto",
- "libgflags",
- "liblog",
- "libprotobuf-cpp-lite",
- "libpuffpatch",
- "libsnapshot_cow",
- "libsparse",
- "libxz",
- "libz",
- "libziparchive",
- "update_metadata-protos",
- ],
- srcs: [
- "make_cow_from_ab_ota.cpp",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- },
-}
-
-cc_binary {
- name: "estimate_cow_from_nonab_ota",
- host_supported: true,
- device_supported: false,
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- static_libs: [
- "libbase",
- "libbrotli",
- "libbz",
- "libcrypto",
- "libgflags",
- "liblog",
- "libsnapshot_cow",
- "libsparse",
- "libz",
- "libziparchive",
- ],
- srcs: [
- "estimate_cow_from_nonab_ota.cpp",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- },
-}
-
-cc_test {
- name: "cow_snapuserd_test",
- defaults: [
- "fs_mgr_defaults",
- ],
- srcs: [
- "cow_snapuserd_test.cpp",
- "snapuserd.cpp",
- "snapuserd_worker.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- ],
- static_libs: [
- "libbrotli",
- "libgtest",
- "libsnapshot_cow",
- "libsnapshot_snapuserd",
- "libcutils_sockets",
- "libz",
- "libfs_mgr",
- "libdm",
- ],
- header_libs: [
- "libstorage_literals_headers",
- "libfiemap_headers",
- ],
- test_min_api_level: 30,
- auto_gen_config: true,
- require_root: false,
-}
-
-cc_binary {
- name: "inspect_cow",
- host_supported: true,
- device_supported: true,
- cflags: [
- "-D_FILE_OFFSET_BITS=64",
- "-Wall",
- "-Werror",
- ],
- static_libs: [
- "libbase",
- "libbrotli",
- "libcrypto_static",
- "liblog",
- "libsnapshot_cow",
- "libz",
- ],
- shared_libs: [
- ],
- srcs: [
- "inspect_cow.cpp",
- ],
-}
diff --git a/fs_mgr/libsnapshot/PowerTest.md b/fs_mgr/libsnapshot/PowerTest.md
deleted file mode 100644
index 0b0cb5dbf..000000000
--- a/fs_mgr/libsnapshot/PowerTest.md
+++ /dev/null
@@ -1,40 +0,0 @@
-snapshot\_power\_test
----------------------
-
-snapshot\_power\_test is a standalone test to simulate power failures during a snapshot-merge operation.
-
-### Test Setup
-
-Start by creating two large files that will be used as the pre-merge and post-merge state. You can take two different partition images (for example, a product.img from two separate builds), or just create random data:
-
- dd if=/dev/urandom of=pre-merge count=1024 bs=1048576
- dd if=/dev/urandom of=post-merge count=1024 bs=1048576
-
-Next, push these files to an unencrypted directory on the device:
-
- adb push pre-merge /data/local/unencrypted
- adb push post-merge /data/local/unencrypted
-
-Next, run the test setup:
-
- adb sync data
- adb shell /data/nativetest64/snapshot_power_test/snapshot_power_test \
- /data/local/unencrypted/pre-merge \
- /data/local/unencrypted/post-merge
-
-This will create the necessary fiemap-based images.
-
-### Running
-The actual test can be run via `run_power_test.sh`. Its syntax is:
-
- run_power_test.sh <POST_MERGE_FILE>
-
-`POST_MERGE_FILE` should be the path on the device of the image to validate the merge against. Example:
-
- run_power_test.sh /data/local/unencrypted/post-merge
-
-The device will begin the merge with a 5% chance of injecting a kernel crash every 10ms. The device should be capable of rebooting normally without user intervention. Once the merge has completed, the test will run a final check command to validate the contents of the snapshot against the post-merge file. It will error if there are any incorrect blocks.
-
-Two environment variables can be passed to `run_power_test.sh`:
-1. `FAIL_RATE` - A fraction between 0 and 100 (inclusive) indicating the probability the device should inject a kernel crash every 10ms.
-2. `DEVICE_SERIAL` - If multiple devices are attached to adb, this argument is passed as the serial to select (to `adb -s`).
diff --git a/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml b/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml
new file mode 100644
index 000000000..b53b51e88
--- /dev/null
+++ b/fs_mgr/libsnapshot/VtsLibsnapshotTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VTS VtsLibsnapshotTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel"/>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsLibsnapshotTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"/>
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"/>
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="5m"/>
+ </test>
+</configuration>
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index de8768c71..032813279 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -34,19 +34,7 @@ enum SnapshotState {
MERGE_COMPLETED = 3;
}
-// Next: 3
-enum MergePhase {
- // No merge is in progress.
- NO_MERGE = 0;
-
- // Shrunk partitions can merge.
- FIRST_PHASE = 1;
-
- // Grown partitions can merge.
- SECOND_PHASE = 2;
-}
-
-// Next: 13
+// Next: 9
message SnapshotStatus {
// Name of the snapshot. This is usually the name of the snapshotted
// logical partition; for example, "system_b".
@@ -96,18 +84,6 @@ message SnapshotStatus {
// the merge process.
// This is non-zero when |state| == MERGING or MERGE_COMPLETED.
uint64 metadata_sectors = 8;
-
- // True if compression is enabled, false otherwise.
- bool compression_enabled = 9;
-
- // The old partition size (if none existed, this will be zero).
- uint64 old_partition_size = 10;
-
- // Compression algorithm (none, gz, or brotli).
- string compression_algorithm = 11;
-
- // Estimated COW size from OTA manifest.
- uint64 estimated_cow_size = 12;
}
// Next: 8
@@ -139,35 +115,7 @@ enum UpdateState {
Cancelled = 7;
};
-// Next 14:
-//
-// To understand the source of each failure, read snapshot.cpp. To handle new
-// sources of failure, avoid reusing an existing code; add a new code instead.
-enum MergeFailureCode {
- Ok = 0;
- ReadStatus = 1;
- GetTableInfo = 2;
- UnknownTable = 3;
- GetTableParams = 4;
- ActivateNewTable = 5;
- AcquireLock = 6;
- ListSnapshots = 7;
- WriteStatus = 8;
- UnknownTargetType = 9;
- QuerySnapshotStatus = 10;
- ExpectedMergeTarget = 11;
- UnmergedSectorsAfterCompletion = 12;
- UnexpectedMergeState = 13;
- GetCowPathConsistencyCheck = 14;
- OpenCowConsistencyCheck = 15;
- ParseCowConsistencyCheck = 16;
- OpenCowDirectConsistencyCheck = 17;
- MemAlignConsistencyCheck = 18;
- DirectReadConsistencyCheck = 19;
- WrongMergeCountConsistencyCheck = 20;
-};
-
-// Next: 8
+// Next: 5
message SnapshotUpdateStatus {
UpdateState state = 1;
@@ -182,21 +130,9 @@ message SnapshotUpdateStatus {
// Sectors allocated for metadata in all the snapshot devices.
uint64 metadata_sectors = 4;
-
- // Whether compression/dm-user was used for any snapshots.
- bool compression_enabled = 5;
-
- // Merge phase (if state == MERGING).
- MergePhase merge_phase = 6;
-
- // Merge failure code, filled if state == MergeFailed.
- MergeFailureCode merge_failure_code = 7;
-
- // Source build fingerprint.
- string source_build_fingerprint = 8;
}
-// Next: 10
+// Next: 4
message SnapshotMergeReport {
// Status of the update after the merge attempts.
UpdateState state = 1;
@@ -207,25 +143,4 @@ message SnapshotMergeReport {
// Total size of all the COW images before the update.
uint64 cow_file_size = 3;
-
- // Whether compression/dm-user was used for any snapshots.
- bool compression_enabled = 4;
-
- // Total size used by COWs, including /data and the super partition.
- uint64 total_cow_size_bytes = 5;
-
- // Sum of the estimated COW fields in the OTA manifest.
- uint64 estimated_cow_size_bytes = 6;
-
- // Time from boot to sys.boot_completed, in milliseconds.
- uint32 boot_complete_time_ms = 7;
-
- // Time from sys.boot_completed to merge start, in milliseconds.
- uint32 boot_complete_to_merge_start_time_ms = 8;
-
- // Merge failure code, filled if state == MergeFailed.
- MergeFailureCode merge_failure_code = 9;
-
- // The source fingerprint at the time the OTA was downloaded.
- string source_build_fingerprint = 10;
}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
deleted file mode 100644
index a55b42acb..000000000
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-syntax = "proto3";
-package android.snapshot;
-
-import "update_engine/update_metadata.proto";
-
-// Controls the behavior of IDeviceInfo.
-// Next: 6
-message FuzzDeviceInfoData {
- bool slot_suffix_is_a = 1;
- bool is_overlayfs_setup = 2;
- bool allow_set_boot_control_merge_status = 3;
- bool allow_set_slot_as_unbootable = 4;
- bool is_recovery = 5;
-}
-
-// Controls the behavior of the test SnapshotManager.
-// Next: 2
-message FuzzSnapshotManagerData {
- bool is_local_image_manager = 1;
-}
-
-// A simplified version of CreateLogicalPartitionParams for fuzzing.
-// Next: 9
-message CreateLogicalPartitionParamsProto {
- bool use_correct_super = 1;
- string block_device = 2;
- bool has_metadata_slot = 3;
- uint32 metadata_slot = 4;
- string partition_name = 5;
- bool force_writable = 6;
- int64 timeout_millis = 7;
- string device_name = 8;
-}
-
-// Mimics the API of ISnapshotManager. Defines one action on the snapshot
-// manager.
-// Next: 18
-message SnapshotManagerActionProto {
- message NoArgs {}
- message ProcessUpdateStateArgs {
- bool has_before_cancel = 1;
- bool fail_before_cancel = 2;
- }
- message CreateLogicalAndSnapshotPartitionsArgs {
- bool use_correct_super = 1;
- string super = 2;
- int64 timeout_millis = 3;
- }
- message RecoveryCreateSnapshotDevicesArgs {
- bool has_metadata_device_object = 1;
- bool metadata_mounted = 2;
- }
- reserved 18 to 9999;
- oneof value {
- NoArgs begin_update = 1;
- NoArgs cancel_update = 2;
- bool finished_snapshot_writes = 3;
- NoArgs initiate_merge = 4;
- ProcessUpdateStateArgs process_update_state = 5;
- bool get_update_state = 6;
- chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7;
- CreateLogicalPartitionParamsProto map_update_snapshot = 8;
- string unmap_update_snapshot = 9;
- NoArgs need_snapshots_in_first_stage_mount = 10;
- CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11;
- bool handle_imminent_data_wipe = 12;
- NoArgs recovery_create_snapshot_devices = 13;
- RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14;
- NoArgs dump = 15;
- NoArgs ensure_metadata_mounted = 16;
- NoArgs get_snapshot_merge_stats_instance = 17;
-
- // Test directives that has nothing to do with ISnapshotManager API surface.
- NoArgs switch_slot = 10000;
- }
-}
-
-// Includes all data that needs to be fuzzed.
-message SnapshotFuzzData {
- FuzzDeviceInfoData device_info_data = 1;
- FuzzSnapshotManagerData manager_data = 2;
-
- // If true:
- // - if super_data is empty, create empty super partition metadata.
- // - otherwise, create super partition metadata accordingly.
- // If false, no valid super partition metadata (it is zeroed)
- bool is_super_metadata_valid = 3;
- chromeos_update_engine.DeltaArchiveManifest super_data = 4;
-
- // Whether the directory that mocks /metadata/ota/snapshot is created.
- bool has_metadata_snapshots_dir = 5;
-
- // More data used to prep the test before running actions.
- reserved 6 to 9999;
- repeated SnapshotManagerActionProto actions = 10000;
-}
diff --git a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt b/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt
deleted file mode 100644
index c474f4ccf..000000000
--- a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-device_info_data {
- allow_set_slot_as_unbootable: true
- is_recovery: true
-}
-is_super_metadata_valid: true
-super_data {
- partitions {
- partition_name: "sys_a"
- new_partition_info {
- size: 3145728
- }
- }
- partitions {
- partition_name: "vnnd_"
- new_partition_info {
- size: 3145728
- }
- }
- partitions {
- partition_name: "prd_a"
- new_partition_info {
- }
- }
- dynamic_partition_metadata {
- groups {
- name: "group_google_dp_a"
- size: 34375467008
- partition_names: "sys_a"
- partition_names: "vnd_a"
- partition_names: "prd_a"
- }
- }
-}
-has_metadata_snapshots_dir: true
-actions {
- handle_imminent_data_wipe: true
-}
-actions {
- begin_update {
- }
-}
diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt
deleted file mode 100644
index 55a7f2ccd..000000000
--- a/fs_mgr/libsnapshot/corpus/launch_device.txt
+++ /dev/null
@@ -1,161 +0,0 @@
-device_info_data {
- slot_suffix_is_a: true
- is_overlayfs_setup: false
- allow_set_boot_control_merge_status: true
- allow_set_slot_as_unbootable: true
- is_recovery: false
-}
-manager_data {
- is_local_image_manager: false
-}
-is_super_metadata_valid: true
-super_data {
- partitions {
- partition_name: "sys_a"
- new_partition_info {
- size: 3145728
- }
- }
- partitions {
- partition_name: "vnd_a"
- new_partition_info {
- size: 3145728
- }
- }
- partitions {
- partition_name: "prd_a"
- new_partition_info {
- size: 3145728
- }
- }
- dynamic_partition_metadata {
- groups {
- name: "group_google_dp_a"
- size: 15728640
- partition_names: "sys_a"
- partition_names: "vnd_a"
- partition_names: "prd_a"
- }
- }
-}
-has_metadata_snapshots_dir: true
-actions {
- begin_update {
- }
-}
-actions {
- create_update_snapshots {
- partitions {
- partition_name: "sys"
- new_partition_info {
- size: 3878912
- }
- operations {
- type: ZERO,
- dst_extents {
- start_block: 0
- num_blocks: 947
- }
- }
- }
- partitions {
- partition_name: "vnd"
- new_partition_info {
- size: 3878912
- }
- operations {
- type: ZERO,
- dst_extents {
- start_block: 0
- num_blocks: 947
- }
- }
- }
- partitions {
- partition_name: "prd"
- new_partition_info {
- size: 3878912
- }
- operations {
- type: ZERO,
- dst_extents {
- start_block: 0
- num_blocks: 947
- }
- }
- }
- dynamic_partition_metadata {
- groups {
- name: "group_google_dp"
- size: 15728640
- partition_names: "sys"
- partition_names: "vnd"
- partition_names: "prd"
- }
- }
- }
-}
-actions {
- map_update_snapshot {
- use_correct_super: true
- has_metadata_slot: true
- metadata_slot: 1
- partition_name: "sys_b"
- force_writable: true
- timeout_millis: 3000
- }
-}
-actions {
- map_update_snapshot {
- use_correct_super: true
- has_metadata_slot: true
- metadata_slot: 1
- partition_name: "vnd_b"
- force_writable: true
- timeout_millis: 3000
- }
-}
-actions {
- map_update_snapshot {
- use_correct_super: true
- has_metadata_slot: true
- metadata_slot: 1
- partition_name: "prd_b"
- force_writable: true
- timeout_millis: 3000
- }
-}
-actions {
- finished_snapshot_writes: false
-}
-actions {
- unmap_update_snapshot: "sys_b"
-}
-actions {
- unmap_update_snapshot: "vnd_b"
-}
-actions {
- unmap_update_snapshot: "prd_b"
-}
-actions {
- switch_slot {
- }
-}
-actions {
- need_snapshots_in_first_stage_mount {
- }
-}
-actions {
- create_logical_and_snapshot_partitions {
- use_correct_super: true
- timeout_millis: 5000
- }
-}
-actions {
- initiate_merge {
- }
-}
-actions {
- process_update_state {
- }
-}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
deleted file mode 100644
index b75b154bf..000000000
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ /dev/null
@@ -1,990 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <sys/stat.h>
-
-#include <cstdio>
-#include <iostream>
-#include <memory>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
-
-using testing::AssertionFailure;
-using testing::AssertionResult;
-using testing::AssertionSuccess;
-
-namespace android {
-namespace snapshot {
-
-class CowTest : public ::testing::Test {
- protected:
- virtual void SetUp() override {
- cow_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_->fd, 0) << strerror(errno);
- }
-
- virtual void TearDown() override { cow_ = nullptr; }
-
- std::unique_ptr<TemporaryFile> cow_;
-};
-
-// Sink that always appends to the end of a string.
-class StringSink : public IByteSink {
- public:
- void* GetBuffer(size_t requested, size_t* actual) override {
- size_t old_size = stream_.size();
- stream_.resize(old_size + requested, '\0');
- *actual = requested;
- return stream_.data() + old_size;
- }
- bool ReturnData(void*, size_t) override { return true; }
- void Reset() { stream_.clear(); }
-
- std::string& stream() { return stream_; }
-
- private:
- std::string stream_;
-};
-
-TEST_F(CowTest, ReadWrite) {
- CowOptions options;
- options.cluster_ops = 0;
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
-
- ASSERT_TRUE(writer.AddCopy(10, 20));
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
- ASSERT_TRUE(writer.Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- CowHeader header;
- CowFooter footer;
- ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetHeader(&header));
- ASSERT_TRUE(reader.GetFooter(&footer));
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
- ASSERT_EQ(header.block_size, options.block_size);
- ASSERT_EQ(footer.op.num_ops, 4);
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
-
- ASSERT_EQ(op->type, kCowCopyOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
- ASSERT_EQ(op->data_length, 0);
- ASSERT_EQ(op->new_block, 10);
- ASSERT_EQ(op->source, 20);
-
- StringSink sink;
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
-
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
- ASSERT_EQ(op->data_length, 4096);
- ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
-
- // Note: the zero operation gets split into two blocks.
- ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
- ASSERT_EQ(op->data_length, 0);
- ASSERT_EQ(op->new_block, 51);
- ASSERT_EQ(op->source, 0);
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
-
- ASSERT_EQ(op->type, kCowZeroOp);
- ASSERT_EQ(op->compression, kCowCompressNone);
- ASSERT_EQ(op->data_length, 0);
- ASSERT_EQ(op->new_block, 52);
- ASSERT_EQ(op->source, 0);
-
- iter->Next();
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, CompressGz) {
- CowOptions options;
- options.cluster_ops = 0;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
-
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
-
- StringSink sink;
-
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressGz);
- ASSERT_EQ(op->data_length, 56); // compressed!
- ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-
- iter->Next();
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, ClusterCompressGz) {
- CowOptions options;
- options.compression = "gz";
- options.cluster_ops = 2;
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-
- std::string data2 = "More data!";
- data2.resize(options.block_size, '\0');
- ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
-
- ASSERT_TRUE(writer.Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
-
- StringSink sink;
-
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressGz);
- ASSERT_EQ(op->data_length, 56); // compressed!
- ASSERT_EQ(op->new_block, 50);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
-
- ASSERT_EQ(op->type, kCowClusterOp);
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
-
- sink.Reset();
- ASSERT_EQ(op->compression, kCowCompressGz);
- ASSERT_EQ(op->data_length, 41); // compressed!
- ASSERT_EQ(op->new_block, 51);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
-
- ASSERT_EQ(op->type, kCowClusterOp);
-
- iter->Next();
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, CompressTwoBlocks) {
- CowOptions options;
- options.compression = "gz";
- options.cluster_ops = 0;
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size * 2, '\0');
-
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
- iter->Next();
- ASSERT_FALSE(iter->Done());
-
- StringSink sink;
-
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_EQ(op->compression, kCowCompressGz);
- ASSERT_EQ(op->new_block, 51);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
-}
-
-// Only return 1-byte buffers, to stress test the partial read logic in
-// CowReader.
-class HorribleStringSink : public StringSink {
- public:
- void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
-};
-
-class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
-
-TEST_P(CompressionTest, HorribleSink) {
- CowOptions options;
- options.compression = GetParam();
- options.cluster_ops = 0;
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
-
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
- ASSERT_FALSE(iter->Done());
-
- HorribleStringSink sink;
- auto op = &iter->Get();
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-}
-
-INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
-
-TEST_F(CowTest, GetSize) {
- CowOptions options;
- options.cluster_ops = 0;
- CowWriter writer(options);
- if (ftruncate(cow_->fd, 0) < 0) {
- perror("Fails to set temp file size");
- FAIL();
- }
- ASSERT_TRUE(writer.Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
-
- ASSERT_TRUE(writer.AddCopy(10, 20));
- ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
- auto size_before = writer.GetCowSize();
- ASSERT_TRUE(writer.Finalize());
- auto size_after = writer.GetCowSize();
- ASSERT_EQ(size_before, size_after);
- struct stat buf;
-
- ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
- ASSERT_EQ(buf.st_size, writer.GetCowSize());
-}
-
-TEST_F(CowTest, AppendLabelSmall) {
- CowOptions options;
- options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer->AddLabel(3));
- ASSERT_TRUE(writer->Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
-
- std::string data2 = "More data!";
- data2.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
- ASSERT_TRUE(writer->Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- struct stat buf;
- ASSERT_EQ(fstat(cow_->fd, &buf), 0);
- ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
- // Read back both operations, and label.
- CowReader reader;
- uint64_t label;
- ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetLastLabel(&label));
- ASSERT_EQ(label, 3);
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data);
-
- iter->Next();
- sink.Reset();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 3);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
-
- iter->Next();
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendLabelMissing) {
- CowOptions options;
- options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- ASSERT_TRUE(writer->AddLabel(0));
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer->AddLabel(1));
- // Drop the tail end of the last op header, corrupting it.
- ftruncate(cow_->fd, writer->GetCowSize() - sizeof(CowFooter) - 3);
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
-
- ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
- ASSERT_TRUE(writer->Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- struct stat buf;
- ASSERT_EQ(fstat(cow_->fd, &buf), 0);
- ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
- // Read back both operations.
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 0);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
-
- iter->Next();
-
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendExtendedCorrupted) {
- CowOptions options;
- options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- ASSERT_TRUE(writer->AddLabel(5));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size * 2, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer->AddLabel(6));
-
- // fail to write the footer. Cow Format does not know if Label 6 is valid
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- // Get the last known good label
- CowReader label_reader;
- uint64_t label;
- ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
- ASSERT_TRUE(label_reader.GetLastLabel(&label));
- ASSERT_EQ(label, 5);
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
-
- ASSERT_TRUE(writer->Finalize());
-
- struct stat buf;
- ASSERT_EQ(fstat(cow_->fd, &buf), 0);
- ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
- // Read back all valid operations
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
-
- iter->Next();
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendbyLabel) {
- CowOptions options;
- options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size * 2, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-
- ASSERT_TRUE(writer->AddLabel(4));
-
- ASSERT_TRUE(writer->AddZeroBlocks(50, 2));
-
- ASSERT_TRUE(writer->AddLabel(5));
-
- ASSERT_TRUE(writer->AddCopy(5, 6));
-
- ASSERT_TRUE(writer->AddLabel(6));
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 12));
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
-
- // This should drop label 6
- ASSERT_TRUE(writer->Finalize());
-
- struct stat buf;
- ASSERT_EQ(fstat(cow_->fd, &buf), 0);
- ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
- // Read back all ops
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
-
- iter->Next();
- sink.Reset();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data.substr(options.block_size, 2 * options.block_size));
-
- iter->Next();
- sink.Reset();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
-
- iter->Next();
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
-
- iter->Next();
-
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, ClusterTest) {
- CowOptions options;
- options.cluster_ops = 4;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-
- ASSERT_TRUE(writer->AddLabel(4));
-
- ASSERT_TRUE(writer->AddZeroBlocks(50, 2)); // Cluster split in middle
-
- ASSERT_TRUE(writer->AddLabel(5));
-
- ASSERT_TRUE(writer->AddCopy(5, 6));
-
- // Cluster split
-
- ASSERT_TRUE(writer->AddLabel(6));
-
- ASSERT_TRUE(writer->Finalize()); // No data for cluster, so no cluster split needed
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- // Read back all ops
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
-
- iter->Next();
- sink.Reset();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 4);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowZeroOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 5);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowCopyOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 6);
-
- iter->Next();
-
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, ClusterAppendTest) {
- CowOptions options;
- options.cluster_ops = 3;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- ASSERT_TRUE(writer->AddLabel(50));
- ASSERT_TRUE(writer->Finalize()); // Adds a cluster op, should be dropped on append
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
-
- std::string data2 = "More data!";
- data2.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
- ASSERT_TRUE(writer->Finalize()); // Adds a cluster op
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- struct stat buf;
- ASSERT_EQ(fstat(cow_->fd, &buf), 0);
- ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
- // Read back both operations, plus cluster op at end
- CowReader reader;
- uint64_t label;
- ASSERT_TRUE(reader.Parse(cow_->fd));
- ASSERT_TRUE(reader.GetLastLabel(&label));
- ASSERT_EQ(label, 50);
-
- StringSink sink;
-
- auto iter = reader.GetOpIter();
- ASSERT_NE(iter, nullptr);
-
- ASSERT_FALSE(iter->Done());
- auto op = &iter->Get();
- ASSERT_EQ(op->type, kCowLabelOp);
- ASSERT_EQ(op->source, 50);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowReplaceOp);
- ASSERT_TRUE(reader.ReadData(*op, &sink));
- ASSERT_EQ(sink.stream(), data2);
-
- iter->Next();
-
- ASSERT_FALSE(iter->Done());
- op = &iter->Get();
- ASSERT_EQ(op->type, kCowClusterOp);
-
- iter->Next();
-
- ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendAfterFinalize) {
- CowOptions options;
- options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
- ASSERT_TRUE(writer->AddLabel(3));
- ASSERT_TRUE(writer->Finalize());
-
- std::string data2 = "More data!";
- data2.resize(options.block_size, '\0');
- ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
- ASSERT_TRUE(writer->Finalize());
-
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- // COW should be valid.
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-}
-
-AssertionResult WriteDataBlock(CowWriter* writer, uint64_t new_block, std::string data) {
- data.resize(writer->options().block_size, '\0');
- if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
- return AssertionFailure() << "Failed to add raw block";
- }
- return AssertionSuccess();
-}
-
-AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
- const std::string& data) {
- CowHeader header;
- reader->GetHeader(&header);
-
- std::string cmp = data;
- cmp.resize(header.block_size, '\0');
-
- StringSink sink;
- if (!reader->ReadData(op, &sink)) {
- return AssertionFailure() << "Failed to read data block";
- }
- if (cmp != sink.stream()) {
- return AssertionFailure() << "Data blocks did not match, expected " << cmp << ", got "
- << sink.stream();
- }
-
- return AssertionSuccess();
-}
-
-TEST_F(CowTest, ResumeMidCluster) {
- CowOptions options;
- options.cluster_ops = 7;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
- ASSERT_TRUE(writer->AddLabel(1));
- ASSERT_TRUE(writer->Finalize());
- ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
- ASSERT_TRUE(writer->AddLabel(2));
- ASSERT_TRUE(writer->Finalize());
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- size_t num_replace = 0;
- size_t max_in_cluster = 0;
- size_t num_in_cluster = 0;
- size_t num_clusters = 0;
- while (!iter->Done()) {
- const auto& op = iter->Get();
-
- num_in_cluster++;
- max_in_cluster = std::max(max_in_cluster, num_in_cluster);
-
- if (op.type == kCowReplaceOp) {
- num_replace++;
-
- ASSERT_EQ(op.new_block, num_replace);
- ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
- num_in_cluster = 0;
- num_clusters++;
- }
-
- iter->Next();
- }
- ASSERT_EQ(num_replace, 8);
- ASSERT_EQ(max_in_cluster, 7);
- ASSERT_EQ(num_clusters, 2);
-}
-
-TEST_F(CowTest, ResumeEndCluster) {
- CowOptions options;
- int cluster_ops = 5;
- options.cluster_ops = cluster_ops;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
- ASSERT_TRUE(writer->AddLabel(1));
- ASSERT_TRUE(writer->Finalize());
- ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
- ASSERT_TRUE(writer->AddLabel(2));
- ASSERT_TRUE(writer->Finalize());
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- size_t num_replace = 0;
- size_t max_in_cluster = 0;
- size_t num_in_cluster = 0;
- size_t num_clusters = 0;
- while (!iter->Done()) {
- const auto& op = iter->Get();
-
- num_in_cluster++;
- max_in_cluster = std::max(max_in_cluster, num_in_cluster);
-
- if (op.type == kCowReplaceOp) {
- num_replace++;
-
- ASSERT_EQ(op.new_block, num_replace);
- ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
- num_in_cluster = 0;
- num_clusters++;
- }
-
- iter->Next();
- }
- ASSERT_EQ(num_replace, 8);
- ASSERT_EQ(max_in_cluster, cluster_ops);
- ASSERT_EQ(num_clusters, 3);
-}
-
-TEST_F(CowTest, DeleteMidCluster) {
- CowOptions options;
- options.cluster_ops = 7;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
-
- ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
- ASSERT_TRUE(writer->AddLabel(1));
- ASSERT_TRUE(writer->Finalize());
- ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
- ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
- ASSERT_TRUE(writer->Finalize());
- ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
- CowReader reader;
- ASSERT_TRUE(reader.Parse(cow_->fd));
-
- auto iter = reader.GetOpIter();
- size_t num_replace = 0;
- size_t max_in_cluster = 0;
- size_t num_in_cluster = 0;
- size_t num_clusters = 0;
- while (!iter->Done()) {
- const auto& op = iter->Get();
-
- num_in_cluster++;
- max_in_cluster = std::max(max_in_cluster, num_in_cluster);
- if (op.type == kCowReplaceOp) {
- num_replace++;
-
- ASSERT_EQ(op.new_block, num_replace);
- ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
- } else if (op.type == kCowClusterOp) {
- num_in_cluster = 0;
- num_clusters++;
- }
-
- iter->Next();
- }
- ASSERT_EQ(num_replace, 3);
- ASSERT_EQ(max_in_cluster, 5); // 3 data, 1 label, 1 cluster op
- ASSERT_EQ(num_clusters, 1);
-}
-
-} // namespace snapshot
-} // namespace android
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
deleted file mode 100644
index faceafe17..000000000
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "cow_decompress.h"
-
-#include <utility>
-
-#include <android-base/logging.h>
-#include <brotli/decode.h>
-#include <zlib.h>
-
-namespace android {
-namespace snapshot {
-
-class NoDecompressor final : public IDecompressor {
- public:
- bool Decompress(size_t) override;
-};
-
-bool NoDecompressor::Decompress(size_t) {
- size_t stream_remaining = stream_->Size();
- while (stream_remaining) {
- size_t buffer_size = stream_remaining;
- uint8_t* buffer = reinterpret_cast<uint8_t*>(sink_->GetBuffer(buffer_size, &buffer_size));
- if (!buffer) {
- LOG(ERROR) << "Could not acquire buffer from sink";
- return false;
- }
-
- // Read until we can fill the buffer.
- uint8_t* buffer_pos = buffer;
- size_t bytes_to_read = std::min(buffer_size, stream_remaining);
- while (bytes_to_read) {
- size_t read;
- if (!stream_->Read(buffer_pos, bytes_to_read, &read)) {
- return false;
- }
- if (!read) {
- LOG(ERROR) << "Stream ended prematurely";
- return false;
- }
- if (!sink_->ReturnData(buffer_pos, read)) {
- LOG(ERROR) << "Could not return buffer to sink";
- return false;
- }
- buffer_pos += read;
- bytes_to_read -= read;
- stream_remaining -= read;
- }
- }
- return true;
-}
-
-std::unique_ptr<IDecompressor> IDecompressor::Uncompressed() {
- return std::unique_ptr<IDecompressor>(new NoDecompressor());
-}
-
-// Read chunks of the COW and incrementally stream them to the decoder.
-class StreamDecompressor : public IDecompressor {
- public:
- bool Decompress(size_t output_bytes) override;
-
- virtual bool Init() = 0;
- virtual bool DecompressInput(const uint8_t* data, size_t length) = 0;
- virtual bool Done() = 0;
-
- protected:
- bool GetFreshBuffer();
-
- size_t output_bytes_;
- size_t stream_remaining_;
- uint8_t* output_buffer_ = nullptr;
- size_t output_buffer_remaining_ = 0;
-};
-
-static constexpr size_t kChunkSize = 4096;
-
-bool StreamDecompressor::Decompress(size_t output_bytes) {
- if (!Init()) {
- return false;
- }
-
- stream_remaining_ = stream_->Size();
- output_bytes_ = output_bytes;
-
- uint8_t chunk[kChunkSize];
- while (stream_remaining_) {
- size_t read = std::min(stream_remaining_, sizeof(chunk));
- if (!stream_->Read(chunk, read, &read)) {
- return false;
- }
- if (!read) {
- LOG(ERROR) << "Stream ended prematurely";
- return false;
- }
- if (!DecompressInput(chunk, read)) {
- return false;
- }
-
- stream_remaining_ -= read;
-
- if (stream_remaining_ && Done()) {
- LOG(ERROR) << "Decompressor terminated early";
- return false;
- }
- }
- if (!Done()) {
- LOG(ERROR) << "Decompressor expected more bytes";
- return false;
- }
- return true;
-}
-
-bool StreamDecompressor::GetFreshBuffer() {
- size_t request_size = std::min(output_bytes_, kChunkSize);
- output_buffer_ =
- reinterpret_cast<uint8_t*>(sink_->GetBuffer(request_size, &output_buffer_remaining_));
- if (!output_buffer_) {
- LOG(ERROR) << "Could not acquire buffer from sink";
- return false;
- }
- return true;
-}
-
-class GzDecompressor final : public StreamDecompressor {
- public:
- ~GzDecompressor();
-
- bool Init() override;
- bool DecompressInput(const uint8_t* data, size_t length) override;
- bool Done() override { return ended_; }
-
- private:
- z_stream z_ = {};
- bool ended_ = false;
-};
-
-bool GzDecompressor::Init() {
- if (int rv = inflateInit(&z_); rv != Z_OK) {
- LOG(ERROR) << "inflateInit returned error code " << rv;
- return false;
- }
- return true;
-}
-
-GzDecompressor::~GzDecompressor() {
- inflateEnd(&z_);
-}
-
-bool GzDecompressor::DecompressInput(const uint8_t* data, size_t length) {
- z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));
- z_.avail_in = length;
-
- while (z_.avail_in) {
- // If no more output buffer, grab a new buffer.
- if (z_.avail_out == 0) {
- if (!GetFreshBuffer()) {
- return false;
- }
- z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
- z_.avail_out = output_buffer_remaining_;
- }
-
- // Remember the position of the output buffer so we can call ReturnData.
- auto avail_out = z_.avail_out;
-
- // Decompress.
- int rv = inflate(&z_, Z_NO_FLUSH);
- if (rv != Z_OK && rv != Z_STREAM_END) {
- LOG(ERROR) << "inflate returned error code " << rv;
- return false;
- }
-
- size_t returned = avail_out - z_.avail_out;
- if (!sink_->ReturnData(output_buffer_, returned)) {
- LOG(ERROR) << "Could not return buffer to sink";
- return false;
- }
- output_buffer_ += returned;
- output_buffer_remaining_ -= returned;
-
- if (rv == Z_STREAM_END) {
- if (z_.avail_in) {
- LOG(ERROR) << "Gz stream ended prematurely";
- return false;
- }
- ended_ = true;
- return true;
- }
- }
- return true;
-}
-
-std::unique_ptr<IDecompressor> IDecompressor::Gz() {
- return std::unique_ptr<IDecompressor>(new GzDecompressor());
-}
-
-class BrotliDecompressor final : public StreamDecompressor {
- public:
- ~BrotliDecompressor();
-
- bool Init() override;
- bool DecompressInput(const uint8_t* data, size_t length) override;
- bool Done() override { return BrotliDecoderIsFinished(decoder_); }
-
- private:
- BrotliDecoderState* decoder_ = nullptr;
-};
-
-bool BrotliDecompressor::Init() {
- decoder_ = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
- return true;
-}
-
-BrotliDecompressor::~BrotliDecompressor() {
- if (decoder_) {
- BrotliDecoderDestroyInstance(decoder_);
- }
-}
-
-bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
- size_t available_in = length;
- const uint8_t* next_in = data;
-
- bool needs_more_output = false;
- while (available_in || needs_more_output) {
- if (!output_buffer_remaining_ && !GetFreshBuffer()) {
- return false;
- }
-
- auto output_buffer = output_buffer_;
- auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
- &output_buffer_remaining_, &output_buffer_, nullptr);
- if (r == BROTLI_DECODER_RESULT_ERROR) {
- LOG(ERROR) << "brotli decode failed";
- return false;
- }
- if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
- return false;
- }
- needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
- }
- return true;
-}
-
-std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
- return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
deleted file mode 100644
index f4852561f..000000000
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ /dev/null
@@ -1,57 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#pragma once
-
-#include <libsnapshot/cow_reader.h>
-
-namespace android {
-namespace snapshot {
-
-class IByteStream {
- public:
- virtual ~IByteStream() {}
-
- // Read up to |length| bytes, storing the number of bytes read in the out-
- // parameter. If the end of the stream is reached, 0 is returned.
- virtual bool Read(void* buffer, size_t length, size_t* read) = 0;
-
- // Size of the stream.
- virtual size_t Size() const = 0;
-};
-
-class IDecompressor {
- public:
- virtual ~IDecompressor() {}
-
- // Factory methods for decompression methods.
- static std::unique_ptr<IDecompressor> Uncompressed();
- static std::unique_ptr<IDecompressor> Gz();
- static std::unique_ptr<IDecompressor> Brotli();
-
- // |output_bytes| is the expected total number of bytes to sink.
- virtual bool Decompress(size_t output_bytes) = 0;
-
- void set_stream(IByteStream* stream) { stream_ = stream; }
- void set_sink(IByteSink* sink) { sink_ = sink; }
-
- protected:
- IByteStream* stream_ = nullptr;
- IByteSink* sink_ = nullptr;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
deleted file mode 100644
index 0753c492e..000000000
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <libsnapshot/cow_format.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace snapshot {
-
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
- os << "CowOperation(type:";
- if (op.type == kCowCopyOp)
- os << "kCowCopyOp, ";
- else if (op.type == kCowReplaceOp)
- os << "kCowReplaceOp, ";
- else if (op.type == kCowZeroOp)
- os << "kZeroOp, ";
- else if (op.type == kCowFooterOp)
- os << "kCowFooterOp, ";
- else if (op.type == kCowLabelOp)
- os << "kCowLabelOp, ";
- else if (op.type == kCowClusterOp)
- os << "kCowClusterOp ";
- else if (op.type == kCowFooterOp)
- os << "kCowFooterOp ";
- else
- os << (int)op.type << "?,";
- os << "compression:";
- if (op.compression == kCowCompressNone)
- os << "kCowCompressNone, ";
- else if (op.compression == kCowCompressGz)
- os << "kCowCompressGz, ";
- else if (op.compression == kCowCompressBrotli)
- os << "kCowCompressBrotli, ";
- else
- os << (int)op.compression << "?, ";
- os << "data_length:" << op.data_length << ",\t";
- os << "new_block:" << op.new_block << ",\t";
- os << "source:" << op.source << ")";
- return os;
-}
-
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
- if (op.type == kCowClusterOp) {
- return op.source;
- } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
- return op.data_length;
- } else {
- return 0;
- }
-}
-
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
- if (op.type == kCowClusterOp) {
- return cluster_ops * sizeof(CowOperation);
- } else if (cluster_ops == 0) {
- return sizeof(CowOperation);
- } else {
- return 0;
- }
-}
-
-bool IsMetadataOp(const CowOperation& op) {
- switch (op.type) {
- case kCowLabelOp:
- case kCowClusterOp:
- case kCowFooterOp:
- return true;
- default:
- return false;
- }
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
deleted file mode 100644
index 2349e4a06..000000000
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ /dev/null
@@ -1,564 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <limits>
-#include <optional>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <libsnapshot/cow_reader.h>
-#include <zlib.h>
-
-#include "cow_decompress.h"
-
-namespace android {
-namespace snapshot {
-
-CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
-
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, data, length);
- SHA256_Final(out, &c);
-#endif
-}
-
-bool CowReader::InitForMerge(android::base::unique_fd&& fd) {
- owned_fd_ = std::move(fd);
- fd_ = owned_fd_.get();
-
- auto pos = lseek(fd_.get(), 0, SEEK_END);
- if (pos < 0) {
- PLOG(ERROR) << "lseek end failed";
- return false;
- }
- fd_size_ = pos;
-
- if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
- if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
- PLOG(ERROR) << "read header failed";
- return false;
- }
-
- return true;
-}
-
-bool CowReader::Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label) {
- owned_fd_ = std::move(fd);
- return Parse(android::base::borrowed_fd{owned_fd_}, label);
-}
-
-bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
- fd_ = fd;
-
- auto pos = lseek(fd_.get(), 0, SEEK_END);
- if (pos < 0) {
- PLOG(ERROR) << "lseek end failed";
- return false;
- }
- fd_size_ = pos;
-
- if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
- if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
- PLOG(ERROR) << "read header failed";
- return false;
- }
-
- if (header_.magic != kCowMagicNumber) {
- LOG(ERROR) << "Header Magic corrupted. Magic: " << header_.magic
- << "Expected: " << kCowMagicNumber;
- return false;
- }
- if (header_.footer_size != sizeof(CowFooter)) {
- LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
- << sizeof(CowFooter);
- return false;
- }
- if (header_.op_size != sizeof(CowOperation)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
- return false;
- }
- if (header_.op_size != sizeof(CowOperation)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
- return false;
- }
-
- if ((header_.major_version > kCowVersionMajor) || (header_.minor_version != kCowVersionMinor)) {
- LOG(ERROR) << "Header version mismatch";
- LOG(ERROR) << "Major version: " << header_.major_version
- << "Expected: " << kCowVersionMajor;
- LOG(ERROR) << "Minor version: " << header_.minor_version
- << "Expected: " << kCowVersionMinor;
- return false;
- }
-
- return ParseOps(label);
-}
-
-bool CowReader::ParseOps(std::optional<uint64_t> label) {
- uint64_t pos;
-
- // Skip the scratch space
- if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
- LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
- size_t init_offset = header_.header_size + header_.buffer_size;
- pos = lseek(fd_.get(), init_offset, SEEK_SET);
- if (pos != init_offset) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
- }
- } else {
- pos = lseek(fd_.get(), header_.header_size, SEEK_SET);
- if (pos != header_.header_size) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
- }
- // Reading a v1 version of COW which doesn't have buffer_size.
- header_.buffer_size = 0;
- }
-
- auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
- uint64_t current_op_num = 0;
- uint64_t cluster_ops = header_.cluster_ops ?: 1;
- bool done = false;
-
- // Alternating op clusters and data
- while (!done) {
- uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
- if (to_add == 0) break;
- ops_buffer->resize(current_op_num + to_add);
- if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
- to_add * sizeof(CowOperation))) {
- PLOG(ERROR) << "read op failed";
- return false;
- }
- // Parse current cluster to find start of next cluster
- while (current_op_num < ops_buffer->size()) {
- auto& current_op = ops_buffer->data()[current_op_num];
- current_op_num++;
- pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
-
- if (current_op.type == kCowClusterOp) {
- break;
- } else if (current_op.type == kCowLabelOp) {
- last_label_ = {current_op.source};
-
- // If we reach the requested label, stop reading.
- if (label && label.value() == current_op.source) {
- done = true;
- break;
- }
- } else if (current_op.type == kCowFooterOp) {
- footer_.emplace();
- CowFooter* footer = &footer_.value();
- memcpy(&footer_->op, &current_op, sizeof(footer->op));
- off_t offs = lseek(fd_.get(), pos, SEEK_SET);
- if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed";
- return false;
- }
- if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
- LOG(ERROR) << "Could not read COW footer";
- return false;
- }
-
- // Drop the footer from the op stream.
- current_op_num--;
- done = true;
- break;
- }
- }
-
- // Position for next cluster read
- off_t offs = lseek(fd_.get(), pos, SEEK_SET);
- if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed";
- return false;
- }
- ops_buffer->resize(current_op_num);
- }
-
- LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
- // To successfully parse a COW file, we need either:
- // (1) a label to read up to, and for that label to be found, or
- // (2) a valid footer.
- if (label) {
- if (!last_label_) {
- LOG(ERROR) << "Did not find label " << label.value()
- << " while reading COW (no labels found)";
- return false;
- }
- if (last_label_.value() != label.value()) {
- LOG(ERROR) << "Did not find label " << label.value()
- << ", last label=" << last_label_.value();
- return false;
- }
- } else if (!footer_) {
- LOG(ERROR) << "No COW footer found";
- return false;
- }
-
- uint8_t csum[32];
- memset(csum, 0, sizeof(uint8_t) * 32);
-
- if (footer_) {
- if (ops_buffer->size() != footer_->op.num_ops) {
- LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
- << ops_buffer->size();
- return false;
- }
- if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
- LOG(ERROR) << "ops size does not match ";
- return false;
- }
- SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
- if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
- LOG(ERROR) << "ops checksum does not match";
- return false;
- }
- SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
- if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
- LOG(ERROR) << "ops checksum does not match";
- return false;
- }
- }
-
- ops_ = ops_buffer;
- ops_->shrink_to_fit();
-
- return true;
-}
-
-void CowReader::InitializeMerge() {
- uint64_t num_copy_ops = 0;
-
- // Remove all the metadata operations
- ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
- [](CowOperation& op) { return IsMetadataOp(op); }),
- ops_.get()->end());
-
- set_total_data_ops(ops_->size());
- // We will re-arrange the vector in such a way that
- // kernel can batch merge. Ex:
- //
- // Existing COW format; All the copy operations
- // are at the beginning.
- // =======================================
- // Copy-op-1 - cow_op->new_block = 1
- // Copy-op-2 - cow_op->new_block = 2
- // Copy-op-3 - cow_op->new_block = 3
- // Replace-op-4 - cow_op->new_block = 6
- // Replace-op-5 - cow_op->new_block = 4
- // Replace-op-6 - cow_op->new_block = 8
- // Replace-op-7 - cow_op->new_block = 9
- // Zero-op-8 - cow_op->new_block = 7
- // Zero-op-9 - cow_op->new_block = 5
- // =======================================
- //
- // First find the operation which isn't a copy-op
- // and then sort all the operations in descending order
- // with the key being cow_op->new_block (source block)
- //
- // The data-structure will look like:
- //
- // =======================================
- // Copy-op-1 - cow_op->new_block = 1
- // Copy-op-2 - cow_op->new_block = 2
- // Copy-op-3 - cow_op->new_block = 3
- // Replace-op-7 - cow_op->new_block = 9
- // Replace-op-6 - cow_op->new_block = 8
- // Zero-op-8 - cow_op->new_block = 7
- // Replace-op-4 - cow_op->new_block = 6
- // Zero-op-9 - cow_op->new_block = 5
- // Replace-op-5 - cow_op->new_block = 4
- // =======================================
- //
- // Daemon will read the above data-structure in reverse-order
- // when reading metadata. Thus, kernel will get the metadata
- // in the following order:
- //
- // ========================================
- // Replace-op-5 - cow_op->new_block = 4
- // Zero-op-9 - cow_op->new_block = 5
- // Replace-op-4 - cow_op->new_block = 6
- // Zero-op-8 - cow_op->new_block = 7
- // Replace-op-6 - cow_op->new_block = 8
- // Replace-op-7 - cow_op->new_block = 9
- // Copy-op-3 - cow_op->new_block = 3
- // Copy-op-2 - cow_op->new_block = 2
- // Copy-op-1 - cow_op->new_block = 1
- // ===========================================
- //
- // When merging begins, kernel will start from the last
- // metadata which was read: In the above format, Copy-op-1
- // will be the first merge operation.
- //
- // Now, batching of the merge operations happens only when
- // 1: origin block numbers in the base device are contiguous
- // (cow_op->new_block) and,
- // 2: cow block numbers which are assigned by daemon in ReadMetadata()
- // are contiguous. These are monotonically increasing numbers.
- //
- // When both (1) and (2) are true, kernel will batch merge the operations.
- // In the above case, we have to ensure that the copy operations
- // are merged first before replace operations are done. Hence,
- // we will not change the order of copy operations. Since,
- // cow_op->new_block numbers are contiguous, we will ensure that the
- // cow block numbers assigned in ReadMetadata() for these respective copy
- // operations are not contiguous forcing kernel to issue merge for each
- // copy operations without batch merging.
- //
- // For all the other operations viz. Replace and Zero op, the cow block
- // numbers assigned by daemon will be contiguous allowing kernel to batch
- // merge.
- //
- // The final format after assiging COW block numbers by the daemon will
- // look something like:
- //
- // =========================================================
- // Replace-op-5 - cow_op->new_block = 4 cow-block-num = 2
- // Zero-op-9 - cow_op->new_block = 5 cow-block-num = 3
- // Replace-op-4 - cow_op->new_block = 6 cow-block-num = 4
- // Zero-op-8 - cow_op->new_block = 7 cow-block-num = 5
- // Replace-op-6 - cow_op->new_block = 8 cow-block-num = 6
- // Replace-op-7 - cow_op->new_block = 9 cow-block-num = 7
- // Copy-op-3 - cow_op->new_block = 3 cow-block-num = 9
- // Copy-op-2 - cow_op->new_block = 2 cow-block-num = 11
- // Copy-op-1 - cow_op->new_block = 1 cow-block-num = 13
- // ==========================================================
- //
- // Merge sequence will look like:
- //
- // Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }
- // Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
- // Replace-op-4, Zero-op-9, Replace-op-5 }
- //==============================================================
-
- num_copy_ops = FindNumCopyops();
-
- std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
- [](CowOperation& op1, CowOperation& op2) -> bool {
- return op1.new_block > op2.new_block;
- });
-
- if (header_.num_merge_ops > 0) {
- ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
- }
-
- num_copy_ops = FindNumCopyops();
- set_copy_ops(num_copy_ops);
-}
-
-uint64_t CowReader::FindNumCopyops() {
- uint64_t num_copy_ops = 0;
-
- for (uint64_t i = 0; i < ops_->size(); i++) {
- auto& current_op = ops_->data()[i];
- if (current_op.type != kCowCopyOp) {
- break;
- }
- num_copy_ops += 1;
- }
-
- return num_copy_ops;
-}
-
-bool CowReader::GetHeader(CowHeader* header) {
- *header = header_;
- return true;
-}
-
-bool CowReader::GetFooter(CowFooter* footer) {
- if (!footer_) return false;
- *footer = footer_.value();
- return true;
-}
-
-bool CowReader::GetLastLabel(uint64_t* label) {
- if (!last_label_) return false;
- *label = last_label_.value();
- return true;
-}
-
-class CowOpIter final : public ICowOpIter {
- public:
- CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops);
-
- bool Done() override;
- const CowOperation& Get() override;
- void Next() override;
-
- private:
- std::shared_ptr<std::vector<CowOperation>> ops_;
- std::vector<CowOperation>::iterator op_iter_;
-};
-
-CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
- ops_ = ops;
- op_iter_ = ops_.get()->begin();
-}
-
-bool CowOpIter::Done() {
- return op_iter_ == ops_.get()->end();
-}
-
-void CowOpIter::Next() {
- CHECK(!Done());
- op_iter_++;
-}
-
-const CowOperation& CowOpIter::Get() {
- CHECK(!Done());
- return (*op_iter_);
-}
-
-class CowOpReverseIter final : public ICowOpReverseIter {
- public:
- explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
-
- bool Done() override;
- const CowOperation& Get() override;
- void Next() override;
-
- private:
- std::shared_ptr<std::vector<CowOperation>> ops_;
- std::vector<CowOperation>::reverse_iterator op_riter_;
-};
-
-CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
- ops_ = ops;
- op_riter_ = ops_.get()->rbegin();
-}
-
-bool CowOpReverseIter::Done() {
- return op_riter_ == ops_.get()->rend();
-}
-
-void CowOpReverseIter::Next() {
- CHECK(!Done());
- op_riter_++;
-}
-
-const CowOperation& CowOpReverseIter::Get() {
- CHECK(!Done());
- return (*op_riter_);
-}
-
-std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
- return std::make_unique<CowOpIter>(ops_);
-}
-
-std::unique_ptr<ICowOpReverseIter> CowReader::GetRevOpIter() {
- return std::make_unique<CowOpReverseIter>(ops_);
-}
-
-bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
- // Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < header_.header_size || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
- offset + len > fd_size_ - sizeof(CowFooter)) {
- LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
- return false;
- }
- if (lseek(fd_.get(), offset, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek to read raw bytes failed";
- return false;
- }
- ssize_t rv = TEMP_FAILURE_RETRY(::read(fd_.get(), buffer, len));
- if (rv < 0) {
- PLOG(ERROR) << "read failed";
- return false;
- }
- *read = rv;
- return true;
-}
-
-class CowDataStream final : public IByteStream {
- public:
- CowDataStream(CowReader* reader, uint64_t offset, size_t data_length)
- : reader_(reader), offset_(offset), data_length_(data_length) {
- remaining_ = data_length_;
- }
-
- bool Read(void* buffer, size_t length, size_t* read) override {
- size_t to_read = std::min(length, remaining_);
- if (!to_read) {
- *read = 0;
- return true;
- }
- if (!reader_->GetRawBytes(offset_, buffer, to_read, read)) {
- return false;
- }
- offset_ += *read;
- remaining_ -= *read;
- return true;
- }
-
- size_t Size() const override { return data_length_; }
-
- private:
- CowReader* reader_;
- uint64_t offset_;
- size_t data_length_;
- size_t remaining_;
-};
-
-bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
- std::unique_ptr<IDecompressor> decompressor;
- switch (op.compression) {
- case kCowCompressNone:
- decompressor = IDecompressor::Uncompressed();
- break;
- case kCowCompressGz:
- decompressor = IDecompressor::Gz();
- break;
- case kCowCompressBrotli:
- decompressor = IDecompressor::Brotli();
- break;
- default:
- LOG(ERROR) << "Unknown compression type: " << op.compression;
- return false;
- }
-
- CowDataStream stream(this, op.source, op.data_length);
- decompressor->set_stream(&stream);
- decompressor->set_sink(sink);
- return decompressor->Decompress(header_.block_size);
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
deleted file mode 100644
index d09c6e9c9..000000000
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ /dev/null
@@ -1,1138 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <linux/memfd.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <iostream>
-#include <memory>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr/file_wait.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libdm/loop_control.h>
-#include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_client.h>
-#include <storage_literals/storage_literals.h>
-
-#include "snapuserd.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android::storage_literals;
-using android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-using namespace std::chrono_literals;
-using namespace android::dm;
-using namespace std;
-
-static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
-
-class TempDevice {
- public:
- TempDevice(const std::string& name, const DmTable& table)
- : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
- }
- TempDevice(TempDevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
- other.valid_ = false;
- }
- ~TempDevice() {
- if (valid_) {
- dm_.DeleteDevice(name_);
- }
- }
- bool Destroy() {
- if (!valid_) {
- return false;
- }
- valid_ = false;
- return dm_.DeleteDevice(name_);
- }
- const std::string& path() const { return path_; }
- const std::string& name() const { return name_; }
- bool valid() const { return valid_; }
-
- TempDevice(const TempDevice&) = delete;
- TempDevice& operator=(const TempDevice&) = delete;
-
- TempDevice& operator=(TempDevice&& other) noexcept {
- name_ = other.name_;
- valid_ = other.valid_;
- other.valid_ = false;
- return *this;
- }
-
- private:
- DeviceMapper& dm_;
- std::string name_;
- std::string path_;
- bool valid_;
-};
-
-class CowSnapuserdTest final {
- public:
- bool Setup();
- bool SetupOrderedOps();
- bool SetupOrderedOpsInverted();
- bool SetupCopyOverlap_1();
- bool SetupCopyOverlap_2();
- bool Merge();
- void ValidateMerge();
- void ReadSnapshotDeviceAndValidate();
- void Shutdown();
- void MergeInterrupt();
- void MergeInterruptFixed(int duration);
- void MergeInterruptRandomly(int max_duration);
- void ReadDmUserBlockWithoutDaemon();
-
- std::string snapshot_dev() const { return snapshot_dev_->path(); }
-
- static const uint64_t kSectorSize = 512;
-
- private:
- void SetupImpl();
-
- void MergeImpl();
- void SimulateDaemonRestart();
- void StartMerge();
-
- void CreateCowDevice();
- void CreateCowDeviceOrderedOps();
- void CreateCowDeviceOrderedOpsInverted();
- void CreateCowDeviceWithCopyOverlap_1();
- void CreateCowDeviceWithCopyOverlap_2();
- bool SetupDaemon();
- void CreateBaseDevice();
- void InitCowDevice();
- void SetDeviceControlName();
- void InitDaemon();
- void CreateDmUserDevice();
- void StartSnapuserdDaemon();
- void CreateSnapshotDevice();
-
- unique_ptr<LoopDevice> base_loop_;
- unique_ptr<TempDevice> dmuser_dev_;
- unique_ptr<TempDevice> snapshot_dev_;
-
- std::string system_device_ctrl_name_;
- std::string system_device_name_;
-
- unique_fd base_fd_;
- std::unique_ptr<TemporaryFile> cow_system_;
- std::unique_ptr<SnapuserdClient> client_;
- std::unique_ptr<uint8_t[]> orig_buffer_;
- std::unique_ptr<uint8_t[]> merged_buffer_;
- bool setup_ok_ = false;
- bool merge_ok_ = false;
- size_t size_ = 50_MiB;
- int cow_num_sectors_;
- int total_base_size_;
-};
-
-class CowSnapuserdMetadataTest final {
- public:
- void Setup();
- void SetupPartialArea();
- void ValidateMetadata();
- void ValidatePartialFilledArea();
-
- private:
- void InitMetadata();
- void CreateCowDevice();
- void CreateCowPartialFilledArea();
-
- std::unique_ptr<Snapuserd> snapuserd_;
- std::unique_ptr<TemporaryFile> cow_system_;
- size_t size_ = 1_MiB;
-};
-
-static unique_fd CreateTempFile(const std::string& name, size_t size) {
- unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
- if (fd < 0) {
- return {};
- }
- if (size) {
- if (ftruncate(fd, size) < 0) {
- perror("ftruncate");
- return {};
- }
- if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
- perror("fcntl");
- return {};
- }
- }
- return fd;
-}
-
-void CowSnapuserdTest::Shutdown() {
- ASSERT_TRUE(snapshot_dev_->Destroy());
- ASSERT_TRUE(dmuser_dev_->Destroy());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(client_->WaitForDeviceDelete(system_device_ctrl_name_));
- ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
- ASSERT_TRUE(client_->DetachSnapuserd());
-}
-
-bool CowSnapuserdTest::Setup() {
- SetupImpl();
- return setup_ok_;
-}
-
-bool CowSnapuserdTest::SetupOrderedOps() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOps();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupOrderedOpsInverted() {
- CreateBaseDevice();
- CreateCowDeviceOrderedOpsInverted();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupCopyOverlap_1() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_1();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupCopyOverlap_2() {
- CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap_2();
- return SetupDaemon();
-}
-
-bool CowSnapuserdTest::SetupDaemon() {
- SetDeviceControlName();
-
- StartSnapuserdDaemon();
- InitCowDevice();
-
- CreateDmUserDevice();
- InitDaemon();
-
- CreateSnapshotDevice();
- setup_ok_ = true;
-
- return setup_ok_;
-}
-
-void CowSnapuserdTest::StartSnapuserdDaemon() {
- pid_t pid = fork();
- ASSERT_GE(pid, 0);
- if (pid == 0) {
- std::string arg0 = "/system/bin/snapuserd";
- std::string arg1 = "-socket="s + kSnapuserdSocketTest;
- char* const argv[] = {arg0.data(), arg1.data(), nullptr};
- ASSERT_GE(execv(arg0.c_str(), argv), 0);
- } else {
- client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
- ASSERT_NE(client_, nullptr);
- }
-}
-
-void CowSnapuserdTest::CreateBaseDevice() {
- unique_fd rnd_fd;
-
- total_base_size_ = (size_ * 4);
- base_fd_ = CreateTempFile("base_device", total_base_size_);
- ASSERT_GE(base_fd_, 0);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
-
- for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
- ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true);
- }
-
- ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
-
- base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
- ASSERT_TRUE(base_loop_->valid());
-}
-
-void CowSnapuserdTest::ReadSnapshotDeviceAndValidate() {
- unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY));
- ASSERT_TRUE(snapshot_fd > 0);
-
- std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
-
- // COPY
- loff_t offset = 0;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
-
- // REPLACE
- offset += size_;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
-
- // ZERO
- offset += size_;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
-
- // REPLACE
- offset += size_;
- ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
- ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
-}
-
-void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
- size_t x = num_blocks;
- size_t blk_src_copy = 0;
-
- // Create overlapping copy operations
- while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
- x -= 1;
- if (x == 1) {
- break;
- }
- blk_src_copy += 1;
- }
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
-
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
-
- // Read the entire base device
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
- true);
-
- // Merged operations required for validation
- int block_size = 4096;
- x = num_blocks;
- loff_t src_offset = block_size;
- loff_t dest_offset = 0;
-
- while (1) {
- memmove((char*)orig_buffer_.get() + dest_offset, (char*)orig_buffer_.get() + src_offset,
- block_size);
- x -= 1;
- if (x == 1) {
- break;
- }
- src_offset += block_size;
- dest_offset += block_size;
- }
-}
-
-void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
- size_t x = num_blocks;
- size_t blk_src_copy = num_blocks - 1;
-
- // Create overlapping copy operations
- while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
- x -= 1;
- if (x == 0) {
- ASSERT_EQ(blk_src_copy, 0);
- break;
- }
- blk_src_copy -= 1;
- }
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
-
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
-
- // Read the entire base device
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
- true);
-
- // Merged operations
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
- true);
- ASSERT_EQ(android::base::ReadFullyAtOffset(
- base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
- true);
-}
-
-void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
- size_t blk_end_copy = num_blocks * 2;
- size_t source_blk = num_blocks - 1;
- size_t blk_src_copy = blk_end_copy - 1;
-
- size_t x = num_blocks;
- while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
- x -= 1;
- if (x == 0) {
- break;
- }
- source_blk -= 1;
- blk_src_copy -= 1;
- }
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- // Read the entire base device
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
- true);
- // Merged Buffer
- memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
-}
-
-void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
- size_t x = num_blocks;
- size_t source_blk = 0;
- size_t blk_src_copy = num_blocks;
-
- while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
-
- x -= 1;
- if (x == 0) {
- break;
- }
- source_blk += 1;
- blk_src_copy += 1;
- }
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- // Read the entire base device
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
- true);
- // Merged Buffer
- memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
-}
-
-void CowSnapuserdTest::CreateCowDevice() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
- size_t blk_end_copy = num_blocks * 2;
- size_t source_blk = num_blocks - 1;
- size_t blk_src_copy = blk_end_copy - 1;
-
- size_t x = num_blocks;
- while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
- x -= 1;
- if (x == 0) {
- break;
- }
- source_blk -= 1;
- blk_src_copy -= 1;
- }
-
- source_blk = num_blocks;
- blk_src_copy = blk_end_copy;
-
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
- size_t blk_zero_copy_start = source_blk + num_blocks;
- size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
-
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
- size_t blk_random2_replace_start = blk_zero_copy_end;
-
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
- // Construct the buffer required for validation
- orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- std::string zero_buffer(size_, 0);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
- memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
-}
-
-void CowSnapuserdTest::InitCowDevice() {
- cow_num_sectors_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
- base_loop_->device());
- ASSERT_NE(cow_num_sectors_, 0);
-}
-
-void CowSnapuserdTest::SetDeviceControlName() {
- system_device_name_.clear();
- system_device_ctrl_name_.clear();
-
- std::string str(cow_system_->path);
- std::size_t found = str.find_last_of("/\\");
- ASSERT_NE(found, std::string::npos);
- system_device_name_ = str.substr(found + 1);
-
- system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-}
-
-void CowSnapuserdTest::CreateDmUserDevice() {
- DmTable dmuser_table;
- ASSERT_TRUE(dmuser_table.AddTarget(
- std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<TempDevice>(system_device_name_, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
- ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
-}
-
-void CowSnapuserdTest::ReadDmUserBlockWithoutDaemon() {
- DmTable dmuser_table;
- std::string dm_user_name = "dm-test-device";
- unique_fd fd;
-
- // Create a dm-user block device
- ASSERT_TRUE(dmuser_table.AddTarget(std::make_unique<DmTargetUser>(0, 123456, dm_user_name)));
- ASSERT_TRUE(dmuser_table.valid());
-
- dmuser_dev_ = std::make_unique<TempDevice>(dm_user_name, dmuser_table);
- ASSERT_TRUE(dmuser_dev_->valid());
- ASSERT_FALSE(dmuser_dev_->path().empty());
-
- fd.reset(open(dmuser_dev_->path().c_str(), O_RDONLY));
- ASSERT_GE(fd, 0);
-
- std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(1_MiB);
-
- loff_t offset = 0;
- // Every IO should fail as there is no daemon to process the IO
- for (size_t j = 0; j < 10; j++) {
- ASSERT_EQ(ReadFullyAtOffset(fd, (char*)buffer.get() + offset, BLOCK_SZ, offset), false);
-
- offset += BLOCK_SZ;
- }
-
- fd = {};
- ASSERT_TRUE(dmuser_dev_->Destroy());
-}
-
-void CowSnapuserdTest::InitDaemon() {
- bool ok = client_->AttachDmUser(system_device_ctrl_name_);
- ASSERT_TRUE(ok);
-}
-
-void CowSnapuserdTest::CreateSnapshotDevice() {
- DmTable snap_table;
- ASSERT_TRUE(snap_table.AddTarget(std::make_unique<DmTargetSnapshot>(
- 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
- SnapshotStorageMode::Persistent, 8)));
- ASSERT_TRUE(snap_table.valid());
-
- snap_table.set_readonly(true);
-
- snapshot_dev_ = std::make_unique<TempDevice>("cowsnapuserd-test-dm-snapshot", snap_table);
- ASSERT_TRUE(snapshot_dev_->valid());
- ASSERT_FALSE(snapshot_dev_->path().empty());
-}
-
-void CowSnapuserdTest::SetupImpl() {
- CreateBaseDevice();
- CreateCowDevice();
-
- SetDeviceControlName();
-
- StartSnapuserdDaemon();
- InitCowDevice();
-
- CreateDmUserDevice();
- InitDaemon();
-
- CreateSnapshotDevice();
- setup_ok_ = true;
-}
-
-bool CowSnapuserdTest::Merge() {
- MergeImpl();
- return merge_ok_;
-}
-
-void CowSnapuserdTest::StartMerge() {
- DmTable merge_table;
- ASSERT_TRUE(merge_table.AddTarget(std::make_unique<DmTargetSnapshot>(
- 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(),
- SnapshotStorageMode::Merge, 8)));
- ASSERT_TRUE(merge_table.valid());
- ASSERT_EQ(total_base_size_ / kSectorSize, merge_table.num_sectors());
-
- DeviceMapper& dm = DeviceMapper::Instance();
- ASSERT_TRUE(dm.LoadTableAndActivate("cowsnapuserd-test-dm-snapshot", merge_table));
-}
-
-void CowSnapuserdTest::MergeImpl() {
- StartMerge();
- DeviceMapper& dm = DeviceMapper::Instance();
-
- while (true) {
- vector<DeviceMapper::TargetInfo> status;
- ASSERT_TRUE(dm.GetTableStatus("cowsnapuserd-test-dm-snapshot", &status));
- ASSERT_EQ(status.size(), 1);
- ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
- 0);
-
- DmTargetSnapshot::Status merge_status;
- ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
- ASSERT_TRUE(merge_status.error.empty());
- if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
- break;
- }
-
- std::this_thread::sleep_for(250ms);
- }
-
- merge_ok_ = true;
-}
-
-void CowSnapuserdTest::ValidateMerge() {
- merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),
- true);
- ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);
-}
-
-void CowSnapuserdTest::SimulateDaemonRestart() {
- Shutdown();
- std::this_thread::sleep_for(500ms);
- SetDeviceControlName();
- StartSnapuserdDaemon();
- InitCowDevice();
- CreateDmUserDevice();
- InitDaemon();
- CreateSnapshotDevice();
-}
-
-void CowSnapuserdTest::MergeInterruptRandomly(int max_duration) {
- std::srand(std::time(nullptr));
- StartMerge();
-
- for (int i = 0; i < 20; i++) {
- int duration = std::rand() % max_duration;
- std::this_thread::sleep_for(std::chrono::milliseconds(duration));
- SimulateDaemonRestart();
- StartMerge();
- }
-
- SimulateDaemonRestart();
- ASSERT_TRUE(Merge());
-}
-
-void CowSnapuserdTest::MergeInterruptFixed(int duration) {
- StartMerge();
-
- for (int i = 0; i < 25; i++) {
- std::this_thread::sleep_for(std::chrono::milliseconds(duration));
- SimulateDaemonRestart();
- StartMerge();
- }
-
- SimulateDaemonRestart();
- ASSERT_TRUE(Merge());
-}
-
-void CowSnapuserdTest::MergeInterrupt() {
- // Interrupt merge at various intervals
- StartMerge();
- std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(250ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(150ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(100ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(800ms);
- SimulateDaemonRestart();
-
- StartMerge();
- std::this_thread::sleep_for(600ms);
- SimulateDaemonRestart();
-
- ASSERT_TRUE(Merge());
-}
-
-void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- // Area 0 is completely filled with 256 exceptions
- for (int i = 0; i < 256; i++) {
- ASSERT_TRUE(writer.AddCopy(i, 256 + i));
- }
-
- // Area 1 is partially filled with 2 copy ops and 10 zero ops
- ASSERT_TRUE(writer.AddCopy(500, 1000));
- ASSERT_TRUE(writer.AddCopy(501, 1001));
-
- ASSERT_TRUE(writer.AddZeroBlocks(300, 10));
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
-}
-
-void CowSnapuserdMetadataTest::ValidatePartialFilledArea() {
- int area_sz = snapuserd_->GetMetadataAreaSize();
-
- ASSERT_EQ(area_sz, 2);
-
- size_t new_chunk = 263;
- // Verify the partially filled area
- void* buffer = snapuserd_->GetExceptionBuffer(1);
- loff_t offset = 0;
- struct disk_exception* de;
- for (int i = 11; i >= 0; i--) {
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, i);
- offset += sizeof(struct disk_exception);
- new_chunk += 1;
- }
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 0);
- ASSERT_EQ(de->new_chunk, 0);
-}
-
-void CowSnapuserdMetadataTest::SetupPartialArea() {
- CreateCowPartialFilledArea();
- InitMetadata();
-}
-
-void CowSnapuserdMetadataTest::CreateCowDevice() {
- unique_fd rnd_fd;
- loff_t offset = 0;
-
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
- rnd_fd.reset(open("/dev/random", O_RDONLY));
- ASSERT_TRUE(rnd_fd > 0);
-
- std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
- // Fill random data
- for (size_t j = 0; j < (size_ / 1_MiB); j++) {
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
- true);
-
- offset += 1_MiB;
- }
-
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
-
- // Overlapping region. This has to be split
- // into two batch operations
- ASSERT_TRUE(writer.AddCopy(23, 20));
- ASSERT_TRUE(writer.AddCopy(22, 19));
- ASSERT_TRUE(writer.AddCopy(21, 18));
- ASSERT_TRUE(writer.AddCopy(20, 17));
- ASSERT_TRUE(writer.AddCopy(19, 16));
- ASSERT_TRUE(writer.AddCopy(18, 15));
-
- // Contiguous region but blocks in ascending order
- // Daemon has to ensure that these blocks are merged
- // in a batch
- ASSERT_TRUE(writer.AddCopy(50, 75));
- ASSERT_TRUE(writer.AddCopy(51, 76));
- ASSERT_TRUE(writer.AddCopy(52, 77));
- ASSERT_TRUE(writer.AddCopy(53, 78));
-
- // Dis-contiguous region
- ASSERT_TRUE(writer.AddCopy(110, 130));
- ASSERT_TRUE(writer.AddCopy(105, 125));
- ASSERT_TRUE(writer.AddCopy(100, 120));
-
- // Overlap
- ASSERT_TRUE(writer.AddCopy(25, 30));
- ASSERT_TRUE(writer.AddCopy(30, 31));
-
- size_t source_blk = num_blocks;
-
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
- size_t blk_zero_copy_start = source_blk + num_blocks;
-
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
- // Flush operations
- ASSERT_TRUE(writer.Finalize());
-}
-
-void CowSnapuserdMetadataTest::InitMetadata() {
- snapuserd_ = std::make_unique<Snapuserd>("", cow_system_->path, "");
- ASSERT_TRUE(snapuserd_->InitCowDevice());
-}
-
-void CowSnapuserdMetadataTest::Setup() {
- CreateCowDevice();
- InitMetadata();
-}
-
-void CowSnapuserdMetadataTest::ValidateMetadata() {
- int area_sz = snapuserd_->GetMetadataAreaSize();
- ASSERT_EQ(area_sz, 3);
-
- size_t old_chunk;
- size_t new_chunk;
-
- for (int i = 0; i < area_sz; i++) {
- void* buffer = snapuserd_->GetExceptionBuffer(i);
- loff_t offset = 0;
- if (i == 0) {
- old_chunk = 256;
- new_chunk = 2;
- } else if (i == 1) {
- old_chunk = 512;
- new_chunk = 259;
- }
- for (int j = 0; j < 256; j++) {
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-
- if (i != 2) {
- ASSERT_EQ(de->old_chunk, old_chunk);
- ASSERT_EQ(de->new_chunk, new_chunk);
- old_chunk += 1;
- new_chunk += 1;
- } else {
- break;
- }
- offset += sizeof(struct disk_exception);
- }
-
- if (i == 2) {
- // The first 5 copy operation is not batch merged
- // as the sequence is discontiguous
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 30);
- ASSERT_EQ(de->new_chunk, 518);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 25);
- ASSERT_EQ(de->new_chunk, 520);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 100);
- ASSERT_EQ(de->new_chunk, 521);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 105);
- ASSERT_EQ(de->new_chunk, 522);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 110);
- ASSERT_EQ(de->new_chunk, 523);
- offset += sizeof(struct disk_exception);
-
- // The next 4 operations are batch merged as
- // both old and new chunk are contiguous
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 53);
- ASSERT_EQ(de->new_chunk, 524);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 52);
- ASSERT_EQ(de->new_chunk, 525);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 51);
- ASSERT_EQ(de->new_chunk, 526);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 50);
- ASSERT_EQ(de->new_chunk, 527);
- offset += sizeof(struct disk_exception);
-
- // This is handling overlap operation with
- // two batch merge operations.
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 18);
- ASSERT_EQ(de->new_chunk, 528);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 19);
- ASSERT_EQ(de->new_chunk, 529);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 20);
- ASSERT_EQ(de->new_chunk, 530);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 21);
- ASSERT_EQ(de->new_chunk, 532);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 22);
- ASSERT_EQ(de->new_chunk, 533);
- offset += sizeof(struct disk_exception);
-
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 23);
- ASSERT_EQ(de->new_chunk, 534);
- offset += sizeof(struct disk_exception);
-
- // End of metadata
- de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 0);
- ASSERT_EQ(de->new_chunk, 0);
- offset += sizeof(struct disk_exception);
- }
- }
-}
-
-TEST(Snapuserd_Test, Snapshot_Metadata) {
- CowSnapuserdMetadataTest harness;
- harness.Setup();
- harness.ValidateMetadata();
-}
-
-TEST(Snapuserd_Test, Snapshot_Metadata_Overlap) {
- CowSnapuserdMetadataTest harness;
- harness.SetupPartialArea();
- harness.ValidatePartialFilledArea();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Resume) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.Setup());
- harness.MergeInterrupt();
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_IO_TEST) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.Setup());
- harness.ReadSnapshotDeviceAndValidate();
- ASSERT_TRUE(harness.Merge());
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap_1());
- ASSERT_TRUE(harness.Merge());
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_2) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap_2());
- ASSERT_TRUE(harness.Merge());
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap_1());
- harness.MergeInterrupt();
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, ReadDmUserBlockWithoutDaemon) {
- CowSnapuserdTest harness;
- harness.ReadDmUserBlockWithoutDaemon();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupOrderedOps());
- harness.MergeInterruptFixed(300);
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupOrderedOps());
- harness.MergeInterruptRandomly(500);
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupOrderedOpsInverted());
- harness.MergeInterruptFixed(50);
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
- CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupOrderedOpsInverted());
- harness.MergeInterruptRandomly(50);
- harness.ValidateMerge();
- harness.Shutdown();
-}
-
-} // namespace snapshot
-} // namespace android
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
deleted file mode 100644
index 51c00a96a..000000000
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ /dev/null
@@ -1,551 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <limits>
-#include <queue>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <brotli/encode.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
-#include <zlib.h>
-
-namespace android {
-namespace snapshot {
-
-static_assert(sizeof(off_t) == sizeof(uint64_t));
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-
-bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
- if (!ValidateNewBlock(new_block)) {
- return false;
- }
- return EmitCopy(new_block, old_block);
-}
-
-bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- if (size % options_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << options_.block_size;
- return false;
- }
-
- uint64_t num_blocks = size / options_.block_size;
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- return EmitRawBlocks(new_block_start, data, size);
-}
-
-bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- return EmitZeroBlocks(new_block_start, num_blocks);
-}
-
-bool ICowWriter::AddLabel(uint64_t label) {
- return EmitLabel(label);
-}
-
-bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
- if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
- LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
- << options_.max_blocks.value();
- return false;
- }
- return true;
-}
-
-CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
- SetupHeaders();
-}
-
-void CowWriter::SetupHeaders() {
- header_ = {};
- header_.magic = kCowMagicNumber;
- header_.major_version = kCowVersionMajor;
- header_.minor_version = kCowVersionMinor;
- header_.header_size = sizeof(CowHeader);
- header_.footer_size = sizeof(CowFooter);
- header_.op_size = sizeof(CowOperation);
- header_.block_size = options_.block_size;
- header_.num_merge_ops = 0;
- header_.cluster_ops = options_.cluster_ops;
- header_.buffer_size = 0;
- footer_ = {};
- footer_.op.data_length = 64;
- footer_.op.type = kCowFooterOp;
-}
-
-bool CowWriter::ParseOptions() {
- if (options_.compression == "gz") {
- compression_ = kCowCompressGz;
- } else if (options_.compression == "brotli") {
- compression_ = kCowCompressBrotli;
- } else if (options_.compression == "none") {
- compression_ = kCowCompressNone;
- } else if (!options_.compression.empty()) {
- LOG(ERROR) << "unrecognized compression: " << options_.compression;
- return false;
- }
- if (options_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
- return false;
- }
- return true;
-}
-
-bool CowWriter::SetFd(android::base::borrowed_fd fd) {
- if (fd.get() < 0) {
- owned_fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
- if (owned_fd_ < 0) {
- PLOG(ERROR) << "open /dev/null failed";
- return false;
- }
- fd_ = owned_fd_;
- is_dev_null_ = true;
- } else {
- fd_ = fd;
-
- struct stat stat;
- if (fstat(fd.get(), &stat) < 0) {
- PLOG(ERROR) << "fstat failed";
- return false;
- }
- is_block_device_ = S_ISBLK(stat.st_mode);
- }
- return true;
-}
-
-bool CowWriter::Initialize(unique_fd&& fd) {
- owned_fd_ = std::move(fd);
- return Initialize(borrowed_fd{owned_fd_});
-}
-
-bool CowWriter::Initialize(borrowed_fd fd) {
- if (!SetFd(fd) || !ParseOptions()) {
- return false;
- }
-
- return OpenForWrite();
-}
-
-bool CowWriter::InitializeAppend(android::base::unique_fd&& fd, uint64_t label) {
- owned_fd_ = std::move(fd);
- return InitializeAppend(android::base::borrowed_fd{owned_fd_}, label);
-}
-
-bool CowWriter::InitializeAppend(android::base::borrowed_fd fd, uint64_t label) {
- if (!SetFd(fd) || !ParseOptions()) {
- return false;
- }
-
- return OpenForAppend(label);
-}
-
-void CowWriter::InitPos() {
- next_op_pos_ = sizeof(header_) + header_.buffer_size;
- cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
- if (header_.cluster_ops) {
- next_data_pos_ = next_op_pos_ + cluster_size_;
- } else {
- next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
- }
- ops_.clear();
- current_cluster_size_ = 0;
- current_data_size_ = 0;
-}
-
-bool CowWriter::OpenForWrite() {
- // This limitation is tied to the data field size in CowOperation.
- if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
- LOG(ERROR) << "Block size is too large";
- return false;
- }
-
- if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
-
- if (options_.scratch_space) {
- header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;
- }
-
- // Headers are not complete, but this ensures the file is at the right
- // position.
- if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
- PLOG(ERROR) << "write failed";
- return false;
- }
-
- if (options_.scratch_space) {
- // Initialize the scratch space
- std::string data(header_.buffer_size, 0);
- if (!android::base::WriteFully(fd_, data.data(), header_.buffer_size)) {
- PLOG(ERROR) << "writing scratch space failed";
- return false;
- }
- }
-
- if (!Sync()) {
- LOG(ERROR) << "Header sync failed";
- return false;
- }
-
- if (lseek(fd_.get(), sizeof(header_) + header_.buffer_size, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
-
- InitPos();
-
- return true;
-}
-
-bool CowWriter::OpenForAppend(uint64_t label) {
- auto reader = std::make_unique<CowReader>();
- std::queue<CowOperation> toAdd;
-
- if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
- return false;
- }
-
- options_.block_size = header_.block_size;
- options_.cluster_ops = header_.cluster_ops;
-
- // Reset this, since we're going to reimport all operations.
- footer_.op.num_ops = 0;
- InitPos();
-
- auto iter = reader->GetOpIter();
-
- while (!iter->Done()) {
- AddOperation(iter->Get());
- iter->Next();
- }
-
- // Free reader so we own the descriptor position again.
- reader = nullptr;
-
- if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
- return EmitClusterIfNeeded();
-}
-
-bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
- CHECK(!merge_in_progress_);
- CowOperation op = {};
- op.type = kCowCopyOp;
- op.new_block = new_block;
- op.source = old_block;
- return WriteOperation(op);
-}
-
-bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
- CHECK(!merge_in_progress_);
- for (size_t i = 0; i < size / header_.block_size; i++) {
- CowOperation op = {};
- op.type = kCowReplaceOp;
- op.new_block = new_block_start + i;
- op.source = next_data_pos_;
-
- if (compression_) {
- auto data = Compress(iter, header_.block_size);
- if (data.empty()) {
- PLOG(ERROR) << "AddRawBlocks: compression failed";
- return false;
- }
- if (data.size() > std::numeric_limits<uint16_t>::max()) {
- LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
- return false;
- }
- op.compression = compression_;
- op.data_length = static_cast<uint16_t>(data.size());
-
- if (!WriteOperation(op, data.data(), data.size())) {
- PLOG(ERROR) << "AddRawBlocks: write failed";
- return false;
- }
- } else {
- op.data_length = static_cast<uint16_t>(header_.block_size);
- if (!WriteOperation(op, iter, header_.block_size)) {
- PLOG(ERROR) << "AddRawBlocks: write failed";
- return false;
- }
- }
-
- iter += header_.block_size;
- }
- return true;
-}
-
-bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- CHECK(!merge_in_progress_);
- for (uint64_t i = 0; i < num_blocks; i++) {
- CowOperation op = {};
- op.type = kCowZeroOp;
- op.new_block = new_block_start + i;
- op.source = 0;
- WriteOperation(op);
- }
- return true;
-}
-
-bool CowWriter::EmitLabel(uint64_t label) {
- CHECK(!merge_in_progress_);
- CowOperation op = {};
- op.type = kCowLabelOp;
- op.source = label;
- return WriteOperation(op) && Sync();
-}
-
-bool CowWriter::EmitCluster() {
- CowOperation op = {};
- op.type = kCowClusterOp;
- // Next cluster starts after remainder of current cluster and the next data block.
- op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
- return WriteOperation(op);
-}
-
-bool CowWriter::EmitClusterIfNeeded() {
- // If there isn't room for another op and the cluster end op, end the current cluster
- if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
- if (!EmitCluster()) return false;
- }
- return true;
-}
-
-std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
- switch (compression_) {
- case kCowCompressGz: {
- auto bound = compressBound(length);
- auto buffer = std::make_unique<uint8_t[]>(bound);
-
- uLongf dest_len = bound;
- auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
- length, Z_BEST_COMPRESSION);
- if (rv != Z_OK) {
- LOG(ERROR) << "compress2 returned: " << rv;
- return {};
- }
- return std::basic_string<uint8_t>(buffer.get(), dest_len);
- }
- case kCowCompressBrotli: {
- auto bound = BrotliEncoderMaxCompressedSize(length);
- if (!bound) {
- LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
- return {};
- }
- auto buffer = std::make_unique<uint8_t[]>(bound);
-
- size_t encoded_size = bound;
- auto rv = BrotliEncoderCompress(
- BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
- reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.get());
- if (!rv) {
- LOG(ERROR) << "BrotliEncoderCompress failed";
- return {};
- }
- return std::basic_string<uint8_t>(buffer.get(), encoded_size);
- }
- default:
- LOG(ERROR) << "unhandled compression type: " << compression_;
- break;
- }
- return {};
-}
-
-// TODO: Fix compilation issues when linking libcrypto library
-// when snapuserd is compiled as part of ramdisk.
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, data, length);
- SHA256_Final(out, &c);
-#endif
-}
-
-bool CowWriter::Finalize() {
- auto continue_cluster_size = current_cluster_size_;
- auto continue_data_size = current_data_size_;
- auto continue_data_pos = next_data_pos_;
- auto continue_op_pos = next_op_pos_;
- auto continue_size = ops_.size();
- auto continue_num_ops = footer_.op.num_ops;
- bool extra_cluster = false;
-
- // Blank out extra ops, in case we're in append mode and dropped ops.
- if (cluster_size_) {
- auto unused_cluster_space = cluster_size_ - current_cluster_size_;
- std::string clr;
- clr.resize(unused_cluster_space, '\0');
- if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
- PLOG(ERROR) << "Failed to seek to footer position.";
- return false;
- }
- if (!android::base::WriteFully(fd_, clr.data(), clr.size())) {
- PLOG(ERROR) << "clearing unused cluster area failed";
- return false;
- }
- }
-
- // Footer should be at the end of a file, so if there is data after the current block, end it
- // and start a new cluster.
- if (cluster_size_ && current_data_size_ > 0) {
- EmitCluster();
- extra_cluster = true;
- }
-
- footer_.op.ops_size = ops_.size();
- if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
- PLOG(ERROR) << "Failed to seek to footer position.";
- return false;
- }
- memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
- memset(&footer_.data.footer_checksum, 0, sizeof(uint8_t) * 32);
-
- SHA256(ops_.data(), ops_.size(), footer_.data.ops_checksum);
- SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
- // Write out footer at end of file
- if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),
- sizeof(footer_))) {
- PLOG(ERROR) << "write footer failed";
- return false;
- }
-
- // Remove excess data, if we're in append mode and threw away more data
- // than we wrote before.
- off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
- if (offs < 0) {
- PLOG(ERROR) << "Failed to lseek to find current position";
- return false;
- }
- if (!Truncate(offs)) {
- return false;
- }
-
- // Reposition for additional Writing
- if (extra_cluster) {
- current_cluster_size_ = continue_cluster_size;
- current_data_size_ = continue_data_size;
- next_data_pos_ = continue_data_pos;
- next_op_pos_ = continue_op_pos;
- footer_.op.num_ops = continue_num_ops;
- ops_.resize(continue_size);
- }
- return Sync();
-}
-
-uint64_t CowWriter::GetCowSize() {
- if (current_data_size_ > 0) {
- return next_data_pos_ + sizeof(footer_);
- } else {
- return next_op_pos_ + sizeof(footer_);
- }
-}
-
-bool CowWriter::GetDataPos(uint64_t* pos) {
- off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
- if (offs < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
- *pos = offs;
- return true;
-}
-
-bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
- if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed for writing operation.";
- return false;
- }
- if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {
- return false;
- }
- if (data != nullptr && size > 0) {
- if (!WriteRawData(data, size)) return false;
- }
- AddOperation(op);
- return EmitClusterIfNeeded();
-}
-
-void CowWriter::AddOperation(const CowOperation& op) {
- footer_.op.num_ops++;
-
- if (op.type == kCowClusterOp) {
- current_cluster_size_ = 0;
- current_data_size_ = 0;
- } else if (header_.cluster_ops) {
- current_cluster_size_ += sizeof(op);
- current_data_size_ += op.data_length;
- }
-
- next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
- next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
- ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
-}
-
-bool CowWriter::WriteRawData(const void* data, size_t size) {
- if (lseek(fd_.get(), next_data_pos_, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed for writing data.";
- return false;
- }
-
- if (!android::base::WriteFully(fd_, data, size)) {
- return false;
- }
- return true;
-}
-
-bool CowWriter::Sync() {
- if (is_dev_null_) {
- return true;
- }
- if (fsync(fd_.get()) < 0) {
- PLOG(ERROR) << "fsync failed";
- return false;
- }
- return true;
-}
-
-bool CowWriter::Truncate(off_t length) {
- if (is_dev_null_ || is_block_device_) {
- return true;
- }
- if (ftruncate(fd_.get(), length) < 0) {
- PLOG(ERROR) << "Failed to truncate.";
- return false;
- }
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 14ce0ee0e..0e9010066 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -17,7 +17,6 @@
#include <android-base/logging.h>
#include <fs_mgr.h>
#include <fs_mgr_overlayfs.h>
-#include <libfiemap/image_manager.h>
namespace android {
namespace snapshot {
@@ -27,7 +26,6 @@ using android::hardware::boot::V1_0::BoolResult;
using android::hardware::boot::V1_0::CommandResult;
#endif
-using namespace std::chrono_literals;
using namespace std::string_literals;
#ifdef __ANDROID_RECOVERY__
@@ -36,6 +34,10 @@ constexpr bool kIsRecovery = true;
constexpr bool kIsRecovery = false;
#endif
+std::string DeviceInfo::GetGsidDir() const {
+ return "ota"s;
+}
+
std::string DeviceInfo::GetMetadataDir() const {
return "/metadata/ota"s;
}
@@ -98,10 +100,6 @@ bool DeviceInfo::IsRecovery() const {
return kIsRecovery;
}
-bool DeviceInfo::IsFirstStageInit() const {
- return first_stage_init_;
-}
-
bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
#ifdef LIBSNAPSHOT_USE_HAL
if (!EnsureBootHal()) {
@@ -122,22 +120,5 @@ bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
#endif
}
-std::unique_ptr<android::fiemap::IImageManager> DeviceInfo::OpenImageManager() const {
- return IDeviceInfo::OpenImageManager("ota");
-}
-
-std::unique_ptr<android::fiemap::IImageManager> ISnapshotManager::IDeviceInfo::OpenImageManager(
- const std::string& gsid_dir) const {
- if (IsRecovery() || IsFirstStageInit()) {
- android::fiemap::ImageManager::DeviceInfo device_info = {
- .is_recovery = {IsRecovery()},
- };
- return android::fiemap::ImageManager::Open(gsid_dir, device_info);
- } else {
- // For now, use a preset timeout.
- return android::fiemap::IImageManager::Open(gsid_dir, 15000ms);
- }
-}
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 7999c99c2..d8d3d91b0 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -29,6 +29,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
public:
+ std::string GetGsidDir() const override;
std::string GetMetadataDir() const override;
std::string GetSlotSuffix() const override;
std::string GetOtherSlotSuffix() const override;
@@ -38,16 +39,11 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
bool SetBootControlMergeStatus(MergeStatus status) override;
bool SetSlotAsUnbootable(unsigned int slot) override;
bool IsRecovery() const override;
- std::unique_ptr<IImageManager> OpenImageManager() const override;
- bool IsFirstStageInit() const override;
-
- void set_first_stage_init(bool value) { first_stage_init_ = value; }
private:
bool EnsureBootHal();
android::fs_mgr::PartitionOpener opener_;
- bool first_stage_init_ = false;
#ifdef LIBSNAPSHOT_USE_HAL
android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
#endif
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index ed77c1526..fef256d03 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -14,10 +14,8 @@
#pragma once
-#include <android-base/logging.h>
#include <stdint.h>
-#include <optional>
#include <vector>
namespace android {
@@ -28,46 +26,19 @@ class DmSnapCowSizeCalculator {
DmSnapCowSizeCalculator(unsigned int sector_bytes, unsigned int chunk_sectors)
: sector_bytes_(sector_bytes),
chunk_sectors_(chunk_sectors),
- exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / exception_size_bytes) {}
+ exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / (64 * 2 / 8)) {}
void WriteByte(uint64_t address) { WriteSector(address / sector_bytes_); }
void WriteSector(uint64_t sector) { WriteChunk(sector / chunk_sectors_); }
void WriteChunk(uint64_t chunk_id) {
- if (!valid_) {
- return;
- }
-
if (modified_chunks_.size() <= chunk_id) {
- if (modified_chunks_.max_size() <= chunk_id) {
- LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
- valid_ = false;
- return;
- }
modified_chunks_.resize(chunk_id + 1, false);
- if (modified_chunks_.size() <= chunk_id) {
- LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
- valid_ = false;
- return;
- }
}
-
modified_chunks_[chunk_id] = true;
}
- std::optional<uint64_t> cow_size_bytes() const {
- auto sectors = cow_size_sectors();
- if (!sectors) {
- return std::nullopt;
- }
- return sectors.value() * sector_bytes_;
- }
- std::optional<uint64_t> cow_size_sectors() const {
- auto chunks = cow_size_chunks();
- if (!chunks) {
- return std::nullopt;
- }
- return chunks.value() * chunk_sectors_;
- }
+ uint64_t cow_size_bytes() const { return cow_size_sectors() * sector_bytes_; }
+ uint64_t cow_size_sectors() const { return cow_size_chunks() * chunk_sectors_; }
/*
* The COW device has a precise internal structure as follows:
@@ -85,12 +56,7 @@ class DmSnapCowSizeCalculator {
* - chunks addressable by previous map (exceptions_per_chunk)
* - 1 extra chunk
*/
- std::optional<uint64_t> cow_size_chunks() const {
- if (!valid_) {
- LOG(ERROR) << "Invalid COW size.";
- return std::nullopt;
- }
-
+ uint64_t cow_size_chunks() const {
uint64_t modified_chunks_count = 0;
uint64_t cow_chunks = 0;
@@ -124,30 +90,19 @@ class DmSnapCowSizeCalculator {
const uint64_t chunk_sectors_;
/*
- * The COW device stores tables to map the modified chunks. Each table has
- * the size of exactly 1 chunk.
- * Each entry of the table is called exception and the number of exceptions
- * that each table can contain determines the number of data chunks that
- * separate two consecutive tables. This value is then fundamental to
- * compute the space overhead introduced by the tables in COW devices.
+ * The COW device stores tables to map the modified chunks. Each table
+ * has the size of exactly 1 chunk.
+ * Each row of the table (also called exception in the kernel) contains two
+ * 64 bit indices to identify the corresponding chunk, and this 128 bit row
+ * size is a constant.
+ * The number of exceptions that each table can contain determines the
+ * number of data chunks that separate two consecutive tables. This value
+ * is then fundamental to compute the space overhead introduced by the
+ * tables in COW devices.
*/
const uint64_t exceptions_per_chunk;
/*
- * Each row of the table (called exception in the kernel) contains two
- * 64 bit indices to identify the corresponding chunk, and this 128 bit
- * pair is constant in size.
- */
- static constexpr unsigned int exception_size_bytes = 64 * 2 / 8;
-
- /*
- * Validity check for the container.
- * It may happen that the caller attempts the write of an invalid chunk
- * identifier, and this misbehavior is accounted and stored in this value.
- */
- bool valid_ = true;
-
- /*
* |modified_chunks_| is a container that keeps trace of the modified
* chunks.
* Multiple options were considered when choosing the most appropriate data
diff --git a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
deleted file mode 100644
index 45833e121..000000000
--- a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
+++ /dev/null
@@ -1,432 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <gflags/gflags.h>
-#include <libsnapshot/cow_writer.h>
-#include <openssl/sha.h>
-#include <sparse/sparse.h>
-#include <ziparchive/zip_archive.h>
-
-DEFINE_string(source_tf, "", "Source target files (dir or zip file)");
-DEFINE_string(ota_tf, "", "Target files of the build for an OTA");
-DEFINE_string(compression, "gz", "Compression (options: none, gz, brotli)");
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-
-static constexpr size_t kBlockSize = 4096;
-
-void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
- unsigned int, const char* message) {
- if (severity == android::base::ERROR) {
- fprintf(stderr, "%s\n", message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-class TargetFilesPackage final {
- public:
- explicit TargetFilesPackage(const std::string& path);
-
- bool Open();
- bool HasFile(const std::string& path);
- std::unordered_set<std::string> GetDynamicPartitionNames();
- unique_fd OpenFile(const std::string& path);
- unique_fd OpenImage(const std::string& path);
-
- private:
- std::string path_;
- unique_fd fd_;
- std::unique_ptr<ZipArchive, decltype(&CloseArchive)> zip_;
-};
-
-TargetFilesPackage::TargetFilesPackage(const std::string& path)
- : path_(path), zip_(nullptr, &CloseArchive) {}
-
-bool TargetFilesPackage::Open() {
- fd_.reset(open(path_.c_str(), O_RDONLY));
- if (fd_ < 0) {
- PLOG(ERROR) << "open failed: " << path_;
- return false;
- }
-
- struct stat s;
- if (fstat(fd_.get(), &s) < 0) {
- PLOG(ERROR) << "fstat failed: " << path_;
- return false;
- }
- if (S_ISDIR(s.st_mode)) {
- return true;
- }
-
- // Otherwise, assume it's a zip file.
- ZipArchiveHandle handle;
- if (OpenArchiveFd(fd_.get(), path_.c_str(), &handle, false)) {
- LOG(ERROR) << "Could not open " << path_ << " as a zip archive.";
- return false;
- }
- zip_.reset(handle);
- return true;
-}
-
-bool TargetFilesPackage::HasFile(const std::string& path) {
- if (zip_) {
- ZipEntry64 entry;
- return !FindEntry(zip_.get(), path, &entry);
- }
-
- auto full_path = path_ + "/" + path;
- return access(full_path.c_str(), F_OK) == 0;
-}
-
-unique_fd TargetFilesPackage::OpenFile(const std::string& path) {
- if (!zip_) {
- auto full_path = path_ + "/" + path;
- unique_fd fd(open(full_path.c_str(), O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << full_path;
- return {};
- }
- return fd;
- }
-
- ZipEntry64 entry;
- if (FindEntry(zip_.get(), path, &entry)) {
- LOG(ERROR) << path << " not found in archive: " << path_;
- return {};
- }
-
- TemporaryFile temp;
- if (temp.fd < 0) {
- PLOG(ERROR) << "mkstemp failed";
- return {};
- }
-
- LOG(INFO) << "Extracting " << path << " from " << path_ << " ...";
- if (ExtractEntryToFile(zip_.get(), &entry, temp.fd)) {
- LOG(ERROR) << "could not extract " << path << " from " << path_;
- return {};
- }
- if (lseek(temp.fd, 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return {};
- }
- return unique_fd{temp.release()};
-}
-
-unique_fd TargetFilesPackage::OpenImage(const std::string& path) {
- auto fd = OpenFile(path);
- if (fd < 0) {
- return {};
- }
-
- LOG(INFO) << "Unsparsing " << path << " ...";
- std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
- sparse_file_import(fd.get(), false, false), &sparse_file_destroy);
- if (!s) {
- return fd;
- }
-
- TemporaryFile temp;
- if (temp.fd < 0) {
- PLOG(ERROR) << "mkstemp failed";
- return {};
- }
- if (sparse_file_write(s.get(), temp.fd, false, false, false) < 0) {
- LOG(ERROR) << "sparse_file_write failed";
- return {};
- }
- if (lseek(temp.fd, 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return {};
- }
-
- fd.reset(temp.release());
- return fd;
-}
-
-std::unordered_set<std::string> TargetFilesPackage::GetDynamicPartitionNames() {
- auto fd = OpenFile("META/misc_info.txt");
- if (fd < 0) {
- return {};
- }
-
- std::string contents;
- if (!android::base::ReadFdToString(fd, &contents)) {
- PLOG(ERROR) << "read failed";
- return {};
- }
-
- std::unordered_set<std::string> set;
-
- auto lines = android::base::Split(contents, "\n");
- for (const auto& line : lines) {
- auto parts = android::base::Split(line, "=");
- if (parts.size() == 2 && parts[0] == "dynamic_partition_list") {
- auto partitions = android::base::Split(parts[1], " ");
- for (const auto& name : partitions) {
- if (!name.empty()) {
- set.emplace(name);
- }
- }
- break;
- }
- }
- return set;
-}
-
-class NonAbEstimator final {
- public:
- NonAbEstimator(const std::string& ota_tf_path, const std::string& source_tf_path)
- : ota_tf_path_(ota_tf_path), source_tf_path_(source_tf_path) {}
-
- bool Run();
-
- private:
- bool OpenPackages();
- bool AnalyzePartition(const std::string& partition_name);
- std::unordered_map<std::string, uint64_t> GetBlockMap(borrowed_fd fd);
-
- std::string ota_tf_path_;
- std::string source_tf_path_;
- std::unique_ptr<TargetFilesPackage> ota_tf_;
- std::unique_ptr<TargetFilesPackage> source_tf_;
- uint64_t size_ = 0;
-};
-
-bool NonAbEstimator::Run() {
- if (!OpenPackages()) {
- return false;
- }
-
- auto partitions = ota_tf_->GetDynamicPartitionNames();
- if (partitions.empty()) {
- LOG(ERROR) << "No dynamic partitions found in META/misc_info.txt";
- return false;
- }
- for (const auto& partition : partitions) {
- if (!AnalyzePartition(partition)) {
- return false;
- }
- }
-
- int64_t size_in_mb = int64_t(double(size_) / 1024.0 / 1024.0);
-
- std::cout << "Estimated COW size: " << size_ << " (" << size_in_mb << "MiB)\n";
- return true;
-}
-
-bool NonAbEstimator::OpenPackages() {
- ota_tf_ = std::make_unique<TargetFilesPackage>(ota_tf_path_);
- if (!ota_tf_->Open()) {
- return false;
- }
- if (!source_tf_path_.empty()) {
- source_tf_ = std::make_unique<TargetFilesPackage>(source_tf_path_);
- if (!source_tf_->Open()) {
- return false;
- }
- }
- return true;
-}
-
-static std::string SHA256(const std::string& input) {
- std::string hash(32, '\0');
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, input.data(), input.size());
- SHA256_Final(reinterpret_cast<unsigned char*>(hash.data()), &c);
- return hash;
-}
-
-bool NonAbEstimator::AnalyzePartition(const std::string& partition_name) {
- auto path = "IMAGES/" + partition_name + ".img";
- auto fd = ota_tf_->OpenImage(path);
- if (fd < 0) {
- return false;
- }
-
- unique_fd source_fd;
- uint64_t source_size = 0;
- std::unordered_map<std::string, uint64_t> source_blocks;
- if (source_tf_) {
- auto dap = source_tf_->GetDynamicPartitionNames();
-
- source_fd = source_tf_->OpenImage(path);
- if (source_fd >= 0) {
- struct stat s;
- if (fstat(source_fd.get(), &s)) {
- PLOG(ERROR) << "fstat failed";
- return false;
- }
- source_size = s.st_size;
-
- std::cout << "Hashing blocks for " << partition_name << "...\n";
- source_blocks = GetBlockMap(source_fd);
- if (source_blocks.empty()) {
- LOG(ERROR) << "Could not build a block map for source partition: "
- << partition_name;
- return false;
- }
- } else {
- if (dap.count(partition_name)) {
- return false;
- }
- LOG(ERROR) << "Warning: " << partition_name
- << " has no incremental diff since it's not in the source image.";
- }
- }
-
- TemporaryFile cow;
- if (cow.fd < 0) {
- PLOG(ERROR) << "mkstemp failed";
- return false;
- }
-
- CowOptions options;
- options.block_size = kBlockSize;
- options.compression = FLAGS_compression;
-
- auto writer = std::make_unique<CowWriter>(options);
- if (!writer->Initialize(borrowed_fd{cow.fd})) {
- LOG(ERROR) << "Could not initialize COW writer";
- return false;
- }
-
- LOG(INFO) << "Analyzing " << partition_name << " ...";
-
- std::string zeroes(kBlockSize, '\0');
- std::string chunk(kBlockSize, '\0');
- std::string src_chunk(kBlockSize, '\0');
- uint64_t next_block_number = 0;
- while (true) {
- if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
- if (errno) {
- PLOG(ERROR) << "read failed";
- return false;
- }
- break;
- }
-
- uint64_t block_number = next_block_number++;
- if (chunk == zeroes) {
- if (!writer->AddZeroBlocks(block_number, 1)) {
- LOG(ERROR) << "Could not add zero block";
- return false;
- }
- continue;
- }
-
- uint64_t source_offset = block_number * kBlockSize;
- if (source_fd >= 0 && source_offset <= source_size) {
- off64_t offset = block_number * kBlockSize;
- if (android::base::ReadFullyAtOffset(source_fd, src_chunk.data(), src_chunk.size(),
- offset)) {
- if (chunk == src_chunk) {
- continue;
- }
- } else if (errno) {
- PLOG(ERROR) << "pread failed";
- return false;
- }
- }
-
- auto hash = SHA256(chunk);
- if (auto iter = source_blocks.find(hash); iter != source_blocks.end()) {
- if (!writer->AddCopy(block_number, iter->second)) {
- return false;
- }
- continue;
- }
-
- if (!writer->AddRawBlocks(block_number, chunk.data(), chunk.size())) {
- return false;
- }
- }
-
- if (!writer->Finalize()) {
- return false;
- }
-
- struct stat s;
- if (fstat(cow.fd, &s) < 0) {
- PLOG(ERROR) << "fstat failed";
- return false;
- }
-
- size_ += s.st_size;
- return true;
-}
-
-std::unordered_map<std::string, uint64_t> NonAbEstimator::GetBlockMap(borrowed_fd fd) {
- std::string chunk(kBlockSize, '\0');
-
- std::unordered_map<std::string, uint64_t> block_map;
- uint64_t block_number = 0;
- while (true) {
- if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
- if (errno) {
- PLOG(ERROR) << "read failed";
- return {};
- }
- break;
- }
- auto hash = SHA256(chunk);
- block_map[hash] = block_number;
- block_number++;
- }
- return block_map;
-}
-
-} // namespace snapshot
-} // namespace android
-
-using namespace android::snapshot;
-
-int main(int argc, char** argv) {
- android::base::InitLogging(argv, android::snapshot::MyLogger);
- gflags::SetUsageMessage("Estimate VAB disk usage from Non A/B builds");
- gflags::ParseCommandLineFlags(&argc, &argv, false);
-
- if (FLAGS_ota_tf.empty()) {
- std::cerr << "Must specify -ota_tf on the command-line." << std::endl;
- return 1;
- }
-
- NonAbEstimator estimator(FLAGS_ota_tf, FLAGS_source_tf);
- if (!estimator.Run()) {
- return 1;
- }
- return 0;
-}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
deleted file mode 100755
index 5995cefde..000000000
--- a/fs_mgr/libsnapshot/fuzz.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/bash
-PROJECT_PATH=system/core/fs_mgr/libsnapshot
-FUZZ_TARGET=libsnapshot_fuzzer
-TARGET_ARCH=$(get_build_var TARGET_ARCH)
-FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
-DEVICE_INIT_CORPUS_DIR=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/corpus
-DEVICE_GENERATED_CORPUS_DIR=/data/local/tmp/${FUZZ_TARGET}/corpus
-DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
-HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
-GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
-
-build_normal() (
- pushd $(gettop)
- NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" NATIVE_COVERAGE_PATHS="" m ${FUZZ_TARGET}
- ret=$?
- popd
- return ${ret}
-)
-
-build_cov() {
- pushd $(gettop)
- NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" NATIVE_COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
- ret=$?
- popd
- return ${ret}
-}
-
-prepare_device() {
- adb root && adb remount &&
- adb shell mkdir -p ${DEVICE_GENERATED_CORPUS_DIR} &&
- adb shell rm -rf ${DEVICE_GCOV_DIR} &&
- adb shell mkdir -p ${DEVICE_GCOV_DIR}
-}
-
-push_binary() {
- adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY} &&
- adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${FUZZ_BINARY})
-}
-
-prepare_host() {
- which lcov || {
- echo "please run:";
- echo " sudo apt-get install lcov ";
- return 1;
- }
- rm -rf ${HOST_SCRATCH_DIR} &&
- mkdir -p ${HOST_SCRATCH_DIR}
-}
-
-# run_snapshot_fuzz -runs=10000
-generate_corpus() {
- [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
-
- prepare_device &&
- build_normal &&
- push_binary &&
- adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
-}
-
-run_snapshot_fuzz() {
- prepare_device &&
- build_cov &&
- push_binary &&
- adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
- ${FUZZ_BINARY} \
- -runs=0 \
- ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
-}
-
-show_fuzz_result() {
- prepare_host &&
- unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip &&
- adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} &&
- ls ${HOST_SCRATCH_DIR} &&
- cat > ${GCOV_TOOL} <<< '
-#!/bin/bash
-exec llvm-cov gcov "$@"
-' &&
- chmod +x ${GCOV_TOOL} &&
- lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov &&
- genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html &&
- echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html)
-}
-
-# run_snapshot_fuzz -runs=10000
-run_snapshot_fuzz_all() {
- generate_corpus "$@" &&
- run_snapshot_fuzz &&
- show_fuzz_result
-}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
deleted file mode 100644
index 0263f7e46..000000000
--- a/fs_mgr/libsnapshot/fuzz_utils.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "fuzz_utils.h"
-
-#include <android-base/logging.h>
-
-namespace android::fuzz {
-
-void CheckInternal(bool value, std::string_view msg) {
- CHECK(value) << msg;
-}
-
-const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
- const google::protobuf::Descriptor* action_desc) {
- CHECK(action_desc);
- CHECK(action_desc->oneof_decl_count() == 1)
- << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name()
- << "; only one is expected.";
- auto* oneof_value_desc = action_desc->oneof_decl(0);
- CHECK(oneof_value_desc);
- CHECK(oneof_value_desc->name() == "value")
- << "oneof field has name " << oneof_value_desc->name();
- return oneof_value_desc;
-}
-
-} // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
deleted file mode 100644
index 20b13b2fa..000000000
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <map>
-#include <string>
-#include <string_view>
-
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/message.h>
-#include <google/protobuf/repeated_field.h>
-
-// Utilities for using a protobuf definition to fuzz APIs in a class.
-// Terms:
-// The "fuzzed class" is the C++ class definition whose functions are fuzzed.
-// The "fuzzed object" is an instantiated object of the fuzzed class. It is
-// typically created and destroyed for each test run.
-// An "action" is an operation on the fuzzed object that may mutate its state.
-// This typically involves one function call into the fuzzed object.
-
-namespace android::fuzz {
-
-// CHECK(value) << msg
-void CheckInternal(bool value, std::string_view msg);
-
-// Get the oneof descriptor inside Action
-const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
- const google::protobuf::Descriptor* action_desc);
-
-template <typename Class>
-using FunctionMapImpl =
- std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc)>>;
-
-template <typename Class>
-class FunctionMap : public FunctionMapImpl<Class> {
- public:
- void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
- typename FunctionMapImpl<Class>::mapped_type&& value) {
- auto [it, inserted] = this->emplace(key, std::move(value));
- CheckInternal(inserted,
- "Multiple implementation registered for tag number " + std::to_string(key));
- }
-};
-
-template <typename Action>
-int CheckConsistency() {
- const auto* function_map = Action::GetFunctionMap();
- const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
-
- for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
- const auto* field_desc = action_value_desc->field(field_index);
- CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
- "Missing impl for function " + field_desc->camelcase_name());
- }
- return 0;
-}
-
-// Get the field descriptor for the oneof field in the action message. If no oneof field is set,
-// return nullptr.
-template <typename Action>
-const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
- const typename Action::Proto& action_proto) {
- static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
-
- auto* action_refl = Action::Proto::GetReflection();
- if (!action_refl->HasOneof(action_proto, action_value_desc)) {
- return nullptr;
- }
- return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
-}
-
-template <typename Action>
-void ExecuteActionProto(typename Action::ClassType* module,
- const typename Action::Proto& action_proto) {
- const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
- if (field_desc == nullptr) return;
- auto number = field_desc->number();
- const auto& map = *Action::GetFunctionMap();
- auto it = map.find(number);
- CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
- const auto& func = it->second;
- func(module, action_proto, field_desc);
-}
-
-template <typename Action>
-void ExecuteAllActionProtos(
- typename Action::ClassType* module,
- const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
- for (const auto& proto : action_protos) {
- ExecuteActionProto<Action>(module, proto);
- }
-}
-
-// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
-template <typename T>
-const T* SafeCast(const google::protobuf::Message& message) {
- if (message.GetDescriptor() != T::GetDescriptor()) {
- return nullptr;
- }
- return static_cast<const T*>(&message);
-}
-
-// Cast message to const T&. Abort if type mismatch.
-template <typename T>
-const T& CheckedCast(const google::protobuf::Message& message) {
- const auto* ptr = SafeCast<T>(message);
- CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
- T::GetDescriptor()->name());
- return *ptr;
-}
-
-// A templated way to a primitive field from a message using reflection.
-template <typename T>
-struct PrimitiveGetter;
-#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name) \
- template <> \
- struct PrimitiveGetter<type> { \
- static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
- }
-
-FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
-FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
-FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
-FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
-FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
-FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
-FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
-
-// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
-// with these arguments.
-template <typename FuzzFunction, typename Signature, typename Enabled = void>
-struct ActionPerformerImpl; // undefined
-
-template <typename FuzzFunction, typename MessageProto>
-struct ActionPerformerImpl<
- FuzzFunction, void(const MessageProto&),
- typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
- static typename FuzzFunction::ReturnType Invoke(
- typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc) {
- const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
- action_proto.GetReflection()->GetMessage(action_proto, field_desc));
- return FuzzFunction::ImplBody(module, arg);
- }
-};
-
-template <typename FuzzFunction, typename Primitive>
-struct ActionPerformerImpl<FuzzFunction, void(Primitive),
- typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
- static typename FuzzFunction::ReturnType Invoke(
- typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc) {
- Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
- action_proto, field_desc);
- return FuzzFunction::ImplBody(module, arg);
- }
-};
-
-template <typename FuzzFunction>
-struct ActionPerformerImpl<FuzzFunction, void()> {
- static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
- const google::protobuf::Message&,
- const google::protobuf::FieldDescriptor*) {
- return FuzzFunction::ImplBody(module);
- }
-};
-
-template <typename FuzzFunction>
-struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
- static typename FuzzFunction::ReturnType Invoke(
- typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
- const google::protobuf::FieldDescriptor* field_desc) {
- std::string scratch;
- const std::string& arg = action_proto.GetReflection()->GetStringReference(
- action_proto, field_desc, &scratch);
- return FuzzFunction::ImplBody(module, arg);
- }
-};
-
-template <typename FuzzFunction>
-struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
-
-} // namespace android::fuzz
-
-// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
-//
-// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
-// message FooActionProto {
-// message NoArgs {}
-// oneof value {
-// bool do_foo = 1;
-// NoArgs do_bar = 1;
-// }
-// }
-// Use it to fuzz a C++ class Foo by doing the following:
-// FUZZ_CLASS(Foo, FooAction)
-// After linking functions of Foo to FooAction, execute all actions by:
-// FooAction::ExecuteAll(foo_object, action_protos)
-#define FUZZ_CLASS(Class, Action) \
- class Action { \
- public: \
- using Proto = Action##Proto; \
- using ClassType = Class; \
- using FunctionMap = android::fuzz::FunctionMap<Class>; \
- static FunctionMap* GetFunctionMap() { \
- static Action::FunctionMap map; \
- return &map; \
- } \
- static void ExecuteAll(Class* module, \
- const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
- [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>(); \
- android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos); \
- } \
- }
-
-#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
-#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
-
-// Implement an action defined in protobuf. Example:
-// message FooActionProto {
-// oneof value {
-// bool do_foo = 1;
-// }
-// }
-// class Foo { public: void DoAwesomeFoo(bool arg); };
-// FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
-// module->DoAwesomeFoo(arg);
-// }
-// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
-#define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...) \
- class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \
- public: \
- using ActionType = Action; \
- using ClassType = Action::ClassType; \
- using ReturnType = Return; \
- using Signature = void(__VA_ARGS__); \
- static constexpr const char name[] = #FunctionName; \
- static constexpr const auto tag = \
- Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
- static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__); \
- \
- private: \
- static bool registered_; \
- }; \
- auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \
- auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag; \
- auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
- Action, FunctionName)>::Invoke; \
- Action::GetFunctionMap()->CheckEmplace(tag, func); \
- return true; \
- })(); \
- Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
-
-// Implement a simple action by linking it to the function with the same name. Example:
-// message FooActionProto {
-// message NoArgs {}
-// oneof value {
-// NoArgs do_bar = 1;
-// }
-// }
-// class Foo { public void DoBar(); };
-// FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoBar);
-// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
-// also the name of the function of Foo.
-#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
- FUZZ_FUNCTION(Action, FunctionName, \
- decltype(std::declval<Action::ClassType>().FunctionName()), \
- Action::ClassType* module) { \
- return module->FunctionName(); \
- }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
deleted file mode 100644
index 000e5e14b..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <stdint.h>
-#include <string>
-
-namespace android {
-namespace snapshot {
-
-static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
-static constexpr uint32_t kCowVersionMajor = 2;
-static constexpr uint32_t kCowVersionMinor = 0;
-
-static constexpr uint32_t kCowVersionManifest = 2;
-
-static constexpr uint32_t BLOCK_SZ = 4096;
-static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
-
-// This header appears as the first sequence of bytes in the COW. All fields
-// in the layout are little-endian encoded. The on-disk layout is:
-//
-// +-----------------------+
-// | Header (fixed) |
-// +-----------------------+
-// | Scratch space |
-// +-----------------------+
-// | Operation (variable) |
-// | Data (variable) |
-// +-----------------------+
-// | Footer (fixed) |
-// +-----------------------+
-//
-// The operations begin immediately after the header, and the "raw data"
-// immediately follows the operation which refers to it. While streaming
-// an OTA, we can immediately write the op and data, syncing after each pair,
-// while storing operation metadata in memory. At the end, we compute data and
-// hashes for the footer, which is placed at the tail end of the file.
-//
-// A missing or corrupt footer likely indicates that writing was cut off
-// between writing the last operation/data pair, or the footer itself. In this
-// case, the safest way to proceed is to assume the last operation is faulty.
-
-struct CowHeader {
- uint64_t magic;
- uint16_t major_version;
- uint16_t minor_version;
-
- // Size of this struct.
- uint16_t header_size;
-
- // Size of footer struct
- uint16_t footer_size;
-
- // Size of op struct
- uint16_t op_size;
-
- // The size of block operations, in bytes.
- uint32_t block_size;
-
- // The number of ops to cluster together. 0 For no clustering. Cannot be 1.
- uint32_t cluster_ops;
-
- // Tracks merge operations completed
- uint64_t num_merge_ops;
-
- // Scratch space used during merge
- uint32_t buffer_size;
-} __attribute__((packed));
-
-// This structure is the same size of a normal Operation, but is repurposed for the footer.
-struct CowFooterOperation {
- // The operation code (always kCowFooterOp).
- uint8_t type;
-
- // If this operation reads from the data section of the COW, this contains
- // the compression type of that data (see constants below).
- uint8_t compression;
-
- // Length of Footer Data. Currently 64 for both checksums
- uint16_t data_length;
-
- // The amount of file space used by Cow operations
- uint64_t ops_size;
-
- // The number of cow operations in the file
- uint64_t num_ops;
-} __attribute__((packed));
-
-struct CowFooterData {
- // SHA256 checksums of Footer op
- uint8_t footer_checksum[32];
-
- // SHA256 of the operation sequence.
- uint8_t ops_checksum[32];
-} __attribute__((packed));
-
-// Cow operations are currently fixed-size entries, but this may change if
-// needed.
-struct CowOperation {
- // The operation code (see the constants and structures below).
- uint8_t type;
-
- // If this operation reads from the data section of the COW, this contains
- // the compression type of that data (see constants below).
- uint8_t compression;
-
- // If this operation reads from the data section of the COW, this contains
- // the length.
- uint16_t data_length;
-
- // The block of data in the new image that this operation modifies.
- uint64_t new_block;
-
- // The value of |source| depends on the operation code.
- //
- // For copy operations, this is a block location in the source image.
- //
- // For replace operations, this is a byte offset within the COW's data
- // sections (eg, not landing within the header or metadata). It is an
- // absolute position within the image.
- //
- // For zero operations (replace with all zeroes), this is unused and must
- // be zero.
- //
- // For Label operations, this is the value of the applied label.
- //
- // For Cluster operations, this is the length of the following data region
- uint64_t source;
-} __attribute__((packed));
-
-static_assert(sizeof(CowOperation) == sizeof(CowFooterOperation));
-
-static constexpr uint8_t kCowCopyOp = 1;
-static constexpr uint8_t kCowReplaceOp = 2;
-static constexpr uint8_t kCowZeroOp = 3;
-static constexpr uint8_t kCowLabelOp = 4;
-static constexpr uint8_t kCowClusterOp = 5;
-static constexpr uint8_t kCowFooterOp = -1;
-
-static constexpr uint8_t kCowCompressNone = 0;
-static constexpr uint8_t kCowCompressGz = 1;
-static constexpr uint8_t kCowCompressBrotli = 2;
-
-static constexpr uint8_t kCowReadAheadNotStarted = 0;
-static constexpr uint8_t kCowReadAheadInProgress = 1;
-static constexpr uint8_t kCowReadAheadDone = 2;
-
-struct CowFooter {
- CowFooterOperation op;
- CowFooterData data;
-} __attribute__((packed));
-
-struct ScratchMetadata {
- // Block of data in the image that operation modifies
- // and read-ahead thread stores the modified data
- // in the scratch space
- uint64_t new_block;
- // Offset within the file to read the data
- uint64_t file_offset;
-} __attribute__((packed));
-
-struct BufferState {
- uint8_t read_ahead_state;
-} __attribute__((packed));
-
-// 2MB Scratch space used for read-ahead
-static constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);
-
-std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
-
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
-
-bool IsMetadataOp(const CowOperation& op);
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
deleted file mode 100644
index 669e58ac6..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <optional>
-
-#include <android-base/unique_fd.h>
-#include <libsnapshot/cow_format.h>
-
-namespace android {
-namespace snapshot {
-
-class ICowOpIter;
-class ICowOpReverseIter;
-
-// A ByteSink object handles requests for a buffer of a specific size. It
-// always owns the underlying buffer. It's designed to minimize potential
-// copying as we parse or decompress the COW.
-class IByteSink {
- public:
- virtual ~IByteSink() {}
-
- // Called when the reader has data. The size of the request is given. The
- // sink must return a valid pointer (or null on failure), and return the
- // maximum number of bytes that can be written to the returned buffer.
- //
- // The returned buffer is owned by IByteSink, but must remain valid until
- // the read operation has completed (or the entire buffer has been
- // covered by calls to ReturnData).
- //
- // After calling GetBuffer(), all previous buffers returned are no longer
- // valid.
- //
- // GetBuffer() is intended to be sequential. A returned size of N indicates
- // that the output stream will advance by N bytes, and the ReturnData call
- // indicates that those bytes have been fulfilled. Therefore, it is
- // possible to have ReturnBuffer do nothing, if the implementation doesn't
- // care about incremental writes.
- virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
-
- // Called when a section returned by |GetBuffer| has been filled with data.
- virtual bool ReturnData(void* buffer, size_t length) = 0;
-};
-
-// Interface for reading from a snapuserd COW.
-class ICowReader {
- public:
- virtual ~ICowReader() {}
-
- // Return the file header.
- virtual bool GetHeader(CowHeader* header) = 0;
-
- // Return the file footer.
- virtual bool GetFooter(CowFooter* footer) = 0;
-
- // Return the last valid label
- virtual bool GetLastLabel(uint64_t* label) = 0;
-
- // Return an iterator for retrieving CowOperation entries.
- virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
-
- // Return an reverse iterator for retrieving CowOperation entries.
- virtual std::unique_ptr<ICowOpReverseIter> GetRevOpIter() = 0;
-
- // Get decoded bytes from the data section, handling any decompression.
- // All retrieved data is passed to the sink.
- virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
-};
-
-// Iterate over a sequence of COW operations.
-class ICowOpIter {
- public:
- virtual ~ICowOpIter() {}
-
- // True if there are more items to read, false otherwise.
- virtual bool Done() = 0;
-
- // Read the current operation.
- virtual const CowOperation& Get() = 0;
-
- // Advance to the next item.
- virtual void Next() = 0;
-};
-
-// Reverse Iterate over a sequence of COW operations.
-class ICowOpReverseIter {
- public:
- virtual ~ICowOpReverseIter() {}
-
- // True if there are more items to read, false otherwise.
- virtual bool Done() = 0;
-
- // Read the current operation.
- virtual const CowOperation& Get() = 0;
-
- // Advance to the next item.
- virtual void Next() = 0;
-};
-
-class CowReader : public ICowReader {
- public:
- CowReader();
- ~CowReader() { owned_fd_ = {}; }
-
- // Parse the COW, optionally, up to the given label. If no label is
- // specified, the COW must have an intact footer.
- bool Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label = {});
- bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
-
- bool InitForMerge(android::base::unique_fd&& fd);
-
- bool GetHeader(CowHeader* header) override;
- bool GetFooter(CowFooter* footer) override;
-
- bool GetLastLabel(uint64_t* label) override;
-
- // Create a CowOpIter object which contains footer_.num_ops
- // CowOperation objects. Get() returns a unique CowOperation object
- // whose lifetime depends on the CowOpIter object; the return
- // value of these will never be null.
- std::unique_ptr<ICowOpIter> GetOpIter() override;
- std::unique_ptr<ICowOpReverseIter> GetRevOpIter() override;
-
- bool ReadData(const CowOperation& op, IByteSink* sink) override;
-
- bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
-
- void InitializeMerge();
-
- // Number of copy, replace, and zero ops. Set if InitializeMerge is called.
- void set_total_data_ops(uint64_t size) { total_data_ops_ = size; }
- uint64_t total_data_ops() { return total_data_ops_; }
- // Number of copy ops. Set if InitializeMerge is called.
- void set_copy_ops(uint64_t size) { copy_ops_ = size; }
- uint64_t total_copy_ops() { return copy_ops_; }
-
- void CloseCowFd() { owned_fd_ = {}; }
-
- private:
- bool ParseOps(std::optional<uint64_t> label);
- uint64_t FindNumCopyops();
-
- android::base::unique_fd owned_fd_;
- android::base::borrowed_fd fd_;
- CowHeader header_;
- std::optional<CowFooter> footer_;
- uint64_t fd_size_;
- std::optional<uint64_t> last_label_;
- std::shared_ptr<std::vector<CowOperation>> ops_;
- uint64_t total_data_ops_;
- uint64_t copy_ops_;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
deleted file mode 100644
index f43ea6834..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <stdint.h>
-
-#include <memory>
-#include <optional>
-#include <string>
-
-#include <android-base/unique_fd.h>
-#include <libsnapshot/cow_format.h>
-#include <libsnapshot/cow_reader.h>
-
-namespace android {
-namespace snapshot {
-
-struct CowOptions {
- uint32_t block_size = 4096;
- std::string compression;
-
- // Maximum number of blocks that can be written.
- std::optional<uint64_t> max_blocks;
-
- // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1.
- uint32_t cluster_ops = 200;
-
- bool scratch_space = true;
-};
-
-// Interface for writing to a snapuserd COW. All operations are ordered; merges
-// will occur in the sequence they were added to the COW.
-class ICowWriter {
- public:
- explicit ICowWriter(const CowOptions& options) : options_(options) {}
-
- virtual ~ICowWriter() {}
-
- // Encode an operation that copies the contents of |old_block| to the
- // location of |new_block|.
- bool AddCopy(uint64_t new_block, uint64_t old_block);
-
- // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
- bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
-
- // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
- bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
-
- // Add a label to the op sequence.
- bool AddLabel(uint64_t label);
-
- // Flush all pending writes. This must be called before closing the writer
- // to ensure that the correct headers and footers are written.
- virtual bool Finalize() = 0;
-
- // Return number of bytes the cow image occupies on disk.
- virtual uint64_t GetCowSize() = 0;
-
- // Returns true if AddCopy() operations are supported.
- virtual bool SupportsCopyOperation() const { return true; }
-
- const CowOptions& options() { return options_; }
-
- protected:
- virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
- virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
- virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
- virtual bool EmitLabel(uint64_t label) = 0;
-
- bool ValidateNewBlock(uint64_t new_block);
-
- protected:
- CowOptions options_;
-};
-
-class CowWriter : public ICowWriter {
- public:
- explicit CowWriter(const CowOptions& options);
-
- // Set up the writer.
- // The file starts from the beginning.
- //
- // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
- // computing COW sizes without using storage space.
- bool Initialize(android::base::unique_fd&& fd);
- bool Initialize(android::base::borrowed_fd fd);
- // Set up a writer, assuming that the given label is the last valid label.
- // This will result in dropping any labels that occur after the given on, and will fail
- // if the given label does not appear.
- bool InitializeAppend(android::base::unique_fd&&, uint64_t label);
- bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
-
- bool Finalize() override;
-
- uint64_t GetCowSize() override;
-
- uint32_t GetCowVersion() { return header_.major_version; }
-
- protected:
- virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
- virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- virtual bool EmitLabel(uint64_t label) override;
-
- private:
- bool EmitCluster();
- bool EmitClusterIfNeeded();
- void SetupHeaders();
- bool ParseOptions();
- bool OpenForWrite();
- bool OpenForAppend(uint64_t label);
- bool GetDataPos(uint64_t* pos);
- bool WriteRawData(const void* data, size_t size);
- bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
- void AddOperation(const CowOperation& op);
- std::basic_string<uint8_t> Compress(const void* data, size_t length);
- void InitPos();
-
- bool SetFd(android::base::borrowed_fd fd);
- bool Sync();
- bool Truncate(off_t length);
-
- private:
- android::base::unique_fd owned_fd_;
- android::base::borrowed_fd fd_;
- CowHeader header_{};
- CowFooter footer_{};
- int compression_ = 0;
- uint64_t next_op_pos_ = 0;
- uint64_t next_data_pos_ = 0;
- uint32_t cluster_size_ = 0;
- uint32_t current_cluster_size_ = 0;
- uint64_t current_data_size_ = 0;
- bool is_dev_null_ = false;
- bool merge_in_progress_ = false;
- bool is_block_device_ = false;
-
- // :TODO: this is not efficient, but stringstream ubsan aborts because some
- // bytes overflow a signed char.
- std::basic_string<uint8_t> ops_;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
deleted file mode 100644
index 573a85b24..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <libsnapshot/snapshot.h>
-
-#include <gmock/gmock.h>
-
-namespace android::snapshot {
-
-class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
- public:
- MOCK_METHOD(std::string, GetMetadataDir, (), (const, override));
- MOCK_METHOD(std::string, GetSlotSuffix, (), (const, override));
- MOCK_METHOD(std::string, GetOtherSlotSuffix, (), (const, override));
- MOCK_METHOD(std::string, GetSuperDevice, (uint32_t slot), (const, override));
- MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
- MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
- MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
- MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
- MOCK_METHOD(bool, IsRecovery, (), (const, override));
- MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));
- MOCK_METHOD(std::unique_ptr<android::fiemap::IImageManager>, OpenImageManager, (),
- (const, override));
-};
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
deleted file mode 100644
index ec58cca2e..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <libsnapshot/snapshot.h>
-#include <payload_consumer/file_descriptor.h>
-
-#include <gmock/gmock.h>
-
-namespace android::snapshot {
-
-class MockSnapshotManager : public ISnapshotManager {
- public:
- MOCK_METHOD(bool, BeginUpdate, (), (override));
- MOCK_METHOD(bool, CancelUpdate, (), (override));
- MOCK_METHOD(bool, FinishedSnapshotWrites, (bool wipe), (override));
- MOCK_METHOD(void, UpdateCowStats, (ISnapshotMergeStats * stats), (override));
- MOCK_METHOD(MergeFailureCode, ReadMergeFailureCode, (), (override));
- MOCK_METHOD(bool, InitiateMerge, (), (override));
-
- MOCK_METHOD(UpdateState, ProcessUpdateState,
- (const std::function<bool()>& callback, const std::function<bool()>& before_cancel),
- (override));
- MOCK_METHOD(UpdateState, GetUpdateState, (double* progress), (override));
- MOCK_METHOD(bool, UpdateUsesCompression, (), (override));
- MOCK_METHOD(Return, CreateUpdateSnapshots,
- (const chromeos_update_engine::DeltaArchiveManifest& manifest), (override));
- MOCK_METHOD(bool, MapUpdateSnapshot,
- (const android::fs_mgr::CreateLogicalPartitionParams& params,
- std::string* snapshot_path),
- (override));
- MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
- (const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>&),
- (override));
- MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
- MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
- MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,
- (const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
- (override));
- MOCK_METHOD(bool, MapAllSnapshots, (const std::chrono::milliseconds& timeout_ms), (override));
- MOCK_METHOD(bool, UnmapAllSnapshots, (), (override));
- MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
- MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
- MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
- MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
- (const std::unique_ptr<AutoDevice>& metadata_device), (override));
- MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
- MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
- MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
- MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
-};
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
deleted file mode 100644
index 3d384cc04..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#pragma once
-
-#include <memory>
-
-#include <gmock/gmock.h>
-#include <libsnapshot/snapshot_stats.h>
-
-namespace android::snapshot {
-
-class MockSnapshotMergeStats final : public ISnapshotMergeStats {
- public:
- virtual ~MockSnapshotMergeStats() = default;
- // Called when merge starts or resumes.
- MOCK_METHOD(bool, Start, (), (override));
- MOCK_METHOD(void, set_state, (android::snapshot::UpdateState, bool), (override));
- MOCK_METHOD(void, set_cow_file_size, (uint64_t), ());
- MOCK_METHOD(void, set_total_cow_size_bytes, (uint64_t), (override));
- MOCK_METHOD(void, set_estimated_cow_size_bytes, (uint64_t), (override));
- MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
- MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
- MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
- MOCK_METHOD(void, set_source_build_fingerprint, (const std::string&), (override));
- MOCK_METHOD(uint64_t, cow_file_size, (), (override));
- MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));
- MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));
- MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));
- MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));
- MOCK_METHOD(std::string, source_build_fingerprint, (), (override));
- MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
- MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
- MOCK_METHOD(bool, WriteState, (), (override));
-
- using ISnapshotMergeStats::Result;
- // Return nullptr if any failure.
-};
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 15882b382..b20797870 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -15,7 +15,6 @@
#pragma once
#include <stdint.h>
-#include <unistd.h>
#include <chrono>
#include <map>
@@ -37,8 +36,6 @@
#include <libsnapshot/auto_device.h>
#include <libsnapshot/return.h>
-#include <libsnapshot/snapshot_writer.h>
-#include <libsnapshot/snapuserd_client.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -73,12 +70,9 @@ struct AutoDeleteCowImage;
struct AutoDeleteSnapshot;
struct AutoDeviceList;
struct PartitionCowCreator;
-class ISnapshotMergeStats;
-class SnapshotMergeStats;
class SnapshotStatus;
static constexpr const std::string_view kCowGroupName = "cow";
-static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,
chromeos_update_engine::InstallOperation* optimized);
@@ -89,41 +83,60 @@ enum class CreateResult : unsigned int {
NOT_CREATED,
};
-class ISnapshotManager {
+class SnapshotManager final {
+ using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+ using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+ using LpMetadata = android::fs_mgr::LpMetadata;
+ using MetadataBuilder = android::fs_mgr::MetadataBuilder;
+ using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
+ using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using FiemapStatus = android::fiemap::FiemapStatus;
+
+ friend class SnapshotMergeStats;
+
public:
// Dependency injection for testing.
class IDeviceInfo {
public:
- using IImageManager = android::fiemap::IImageManager;
-
virtual ~IDeviceInfo() {}
+ virtual std::string GetGsidDir() const = 0;
virtual std::string GetMetadataDir() const = 0;
virtual std::string GetSlotSuffix() const = 0;
virtual std::string GetOtherSlotSuffix() const = 0;
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
- virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
+ virtual const IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
- virtual bool SetBootControlMergeStatus(
- android::hardware::boot::V1_1::MergeStatus status) = 0;
+ virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
- virtual bool IsTestDevice() const { return false; }
- virtual bool IsFirstStageInit() const = 0;
- virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
-
- // Helper method for implementing OpenImageManager.
- std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
};
- virtual ~ISnapshotManager() = default;
+
+ ~SnapshotManager();
+
+ // Return a new SnapshotManager instance, or null on error. The device
+ // pointer is owned for the lifetime of SnapshotManager. If null, a default
+ // instance will be created.
+ static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
+
+ // This is similar to New(), except designed specifically for first-stage
+ // init or recovery.
+ static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+ // Helper function for first-stage init to check whether a SnapshotManager
+ // might be needed to perform first-stage mounts.
+ static bool IsSnapshotManagerNeeded();
+
+ // Helper function for second stage init to restorecon on the rollback indicator.
+ static std::string GetGlobalRollbackIndicatorPath();
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
- virtual bool BeginUpdate() = 0;
+ bool BeginUpdate();
// Cancel an update; any snapshots will be deleted. This is allowed if the
// state == Initiated, None, or Unverified (before rebooting to the new
// slot).
- virtual bool CancelUpdate() = 0;
+ bool CancelUpdate();
// Mark snapshot writes as having completed. After this, new snapshots cannot
// be created, and the device must either cancel the OTA (either before
@@ -131,16 +144,11 @@ class ISnapshotManager {
// Before calling this function, all snapshots must be mapped.
// If |wipe| is set to true, wipe is scheduled after reboot, and snapshots
// may need to be merged before wiping.
- virtual bool FinishedSnapshotWrites(bool wipe) = 0;
-
- // Update an ISnapshotMergeStats object with statistics about COW usage.
- // This should be called before the merge begins as otherwise snapshots
- // may be deleted.
- virtual void UpdateCowStats(ISnapshotMergeStats* stats) = 0;
+ bool FinishedSnapshotWrites(bool wipe);
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
- virtual bool InitiateMerge() = 0;
+ bool InitiateMerge(uint64_t* cow_file_size = nullptr);
// Perform any necessary post-boot actions. This should be run soon after
// /data is mounted.
@@ -170,15 +178,8 @@ class ISnapshotManager {
//
// The optional callback allows the caller to periodically check the
// progress with GetUpdateState().
- virtual UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
- const std::function<bool()>& before_cancel = {}) = 0;
-
- // If ProcessUpdateState() returned MergeFailed, this returns the appropriate
- // code. Otherwise, MergeFailureCode::Ok is returned.
- virtual MergeFailureCode ReadMergeFailureCode() = 0;
-
- // If an update is in progress, return the source build fingerprint.
- virtual std::string ReadSourceBuildFingerprint() = 0;
+ UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {});
// Find the status of the current update, if any.
//
@@ -186,59 +187,28 @@ class ISnapshotManager {
// Merging: Value in the range [0, 100]
// MergeCompleted: 100
// Other: 0
- virtual UpdateState GetUpdateState(double* progress = nullptr) = 0;
-
- // Returns true if compression is enabled for the current update. This always returns false if
- // UpdateState is None, or no snapshots have been created.
- virtual bool UpdateUsesCompression() = 0;
+ UpdateState GetUpdateState(double* progress = nullptr);
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
// "write-protected" and snapshotted.
- virtual Return CreateUpdateSnapshots(
- const chromeos_update_engine::DeltaArchiveManifest& manifest) = 0;
+ Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
- //
- // |snapshot_path| must not be nullptr.
- //
- // This method will return false if ro.virtual_ab.compression.enabled is true.
- virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
- std::string* snapshot_path) = 0;
-
- // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
- // must be suffixed. If a source partition exists, it must be specified as well. The source
- // partition will only be used if raw bytes are needed. The source partition should be an
- // absolute path to the device, not a partition name.
- //
- // After calling OpenSnapshotWriter, the caller must invoke Initialize or InitializeForAppend
- // before invoking write operations.
- virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) = 0;
+ bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
- // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
- // OpenSnapshotWriter. All outstanding open descriptors, writers, or
- // readers must be deleted before this is called.
- virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
+ // Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
+ bool UnmapUpdateSnapshot(const std::string& target_partition_name);
// If this returns true, first-stage mount must call
// CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
- virtual bool NeedSnapshotsInFirstStageMount() = 0;
+ bool NeedSnapshotsInFirstStageMount();
// Perform first-stage mapping of snapshot targets. This replaces init's
// call to CreateLogicalPartitions when snapshots are present.
- virtual bool CreateLogicalAndSnapshotPartitions(
- const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;
-
- // Map all snapshots. This is analogous to CreateLogicalAndSnapshotPartitions, except it maps
- // the target slot rather than the current slot. It should only be used immediately after
- // applying an update, before rebooting to the new slot.
- virtual bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) = 0;
-
- // Unmap all snapshots. This should be called to undo MapAllSnapshots().
- virtual bool UnmapAllSnapshots() = 0;
+ bool CreateLogicalAndSnapshotPartitions(const std::string& super_device,
+ const std::chrono::milliseconds& timeout_ms = {});
// This method should be called preceding any wipe or flash of metadata or
// userdata. It is only valid in recovery or fastbootd, and it ensures that
@@ -251,11 +221,11 @@ class ISnapshotManager {
//
// Returns true on success (or nothing to do), false on failure. The
// optional callback fires periodically to query progress via GetUpdateState.
- virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
+ bool HandleImminentDataWipe(const std::function<void()>& callback = {});
// Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
// but does not expect a data wipe after.
- virtual bool FinishMergeInRecovery() = 0;
+ bool FinishMergeInRecovery();
// This method is only allowed in recovery and is used as a helper to
// initialize the snapshot devices as a requirement to mount a snapshotted
@@ -268,15 +238,14 @@ class ISnapshotManager {
// be aborted.
// This function mounts /metadata when called, and unmounts /metadata upon
// return.
- virtual CreateResult RecoveryCreateSnapshotDevices() = 0;
+ CreateResult RecoveryCreateSnapshotDevices();
// Same as RecoveryCreateSnapshotDevices(), but does not auto mount/umount
// /metadata.
- virtual CreateResult RecoveryCreateSnapshotDevices(
- const std::unique_ptr<AutoDevice>& metadata_device) = 0;
+ CreateResult RecoveryCreateSnapshotDevices(const std::unique_ptr<AutoDevice>& metadata_device);
// Dump debug information.
- virtual bool Dump(std::ostream& os) = 0;
+ bool Dump(std::ostream& os);
// Ensure metadata directory is mounted in recovery. When the returned
// AutoDevice is destroyed, the metadata directory is automatically
@@ -292,99 +261,7 @@ class ISnapshotManager {
// auto b = mgr->EnsureMetadataMounted(); // does nothing
// b.reset() // unmounts
// a.reset() // does nothing
- virtual std::unique_ptr<AutoDevice> EnsureMetadataMounted() = 0;
-
- // Return the associated ISnapshotMergeStats instance. Never null.
- virtual ISnapshotMergeStats* GetSnapshotMergeStatsInstance() = 0;
-};
-
-class SnapshotManager final : public ISnapshotManager {
- using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
- using IPartitionOpener = android::fs_mgr::IPartitionOpener;
- using LpMetadata = android::fs_mgr::LpMetadata;
- using MetadataBuilder = android::fs_mgr::MetadataBuilder;
- using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
- using FiemapStatus = android::fiemap::FiemapStatus;
-
- friend class SnapshotMergeStats;
-
- public:
- ~SnapshotManager();
-
- // Return a new SnapshotManager instance, or null on error. The device
- // pointer is owned for the lifetime of SnapshotManager. If null, a default
- // instance will be created.
- static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
-
- // This is similar to New(), except designed specifically for first-stage
- // init or recovery.
- static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
-
- // Helper function for first-stage init to check whether a SnapshotManager
- // might be needed to perform first-stage mounts.
- static bool IsSnapshotManagerNeeded();
-
- // Helper function for second stage init to restorecon on the rollback indicator.
- static std::string GetGlobalRollbackIndicatorPath();
-
- // Detach dm-user devices from the current snapuserd, and populate
- // |snapuserd_argv| with the necessary arguments to restart snapuserd
- // and reattach them.
- bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv);
-
- // Perform the transition from the selinux stage of snapuserd into the
- // second-stage of snapuserd. This process involves re-creating the dm-user
- // table entries for each device, so that they connect to the new daemon.
- // Once all new tables have been activated, we ask the first-stage daemon
- // to cleanly exit.
- bool PerformSecondStageInitTransition();
-
- // ISnapshotManager overrides.
- bool BeginUpdate() override;
- bool CancelUpdate() override;
- bool FinishedSnapshotWrites(bool wipe) override;
- void UpdateCowStats(ISnapshotMergeStats* stats) override;
- MergeFailureCode ReadMergeFailureCode() override;
- bool InitiateMerge() override;
- UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
- const std::function<bool()>& before_cancel = {}) override;
- UpdateState GetUpdateState(double* progress = nullptr) override;
- bool UpdateUsesCompression() override;
- Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
- bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
- std::string* snapshot_path) override;
- std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) override;
- bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
- bool NeedSnapshotsInFirstStageMount() override;
- bool CreateLogicalAndSnapshotPartitions(
- const std::string& super_device,
- const std::chrono::milliseconds& timeout_ms = {}) override;
- bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
- bool FinishMergeInRecovery() override;
- CreateResult RecoveryCreateSnapshotDevices() override;
- CreateResult RecoveryCreateSnapshotDevices(
- const std::unique_ptr<AutoDevice>& metadata_device) override;
- bool Dump(std::ostream& os) override;
- std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
- ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
- bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
- bool UnmapAllSnapshots() override;
- std::string ReadSourceBuildFingerprint() override;
-
- // We can't use WaitForFile during first-stage init, because ueventd is not
- // running and therefore will not automatically create symlinks. Instead,
- // we let init provide us with the correct function to use to ensure
- // uevents have been processed and symlink/mknod calls completed.
- void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
- uevent_regen_callback_ = callback;
- }
-
- // If true, compression is enabled for this update. This is used by
- // first-stage to decide whether to launch snapuserd.
- bool IsSnapuserdRequired();
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted();
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
@@ -396,23 +273,18 @@ class SnapshotManager final : public ISnapshotManager {
FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
FRIEND_TEST(SnapshotTest, MapSnapshot);
FRIEND_TEST(SnapshotTest, Merge);
- FRIEND_TEST(SnapshotTest, MergeFailureCode);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
- FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
- FRIEND_TEST(SnapshotUpdateTest, DataWipeWithStaleSnapshots);
FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
- FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
friend class LockTestConsumer;
- friend class SnapshotFuzzEnv;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
@@ -426,15 +298,11 @@ class SnapshotManager final : public ISnapshotManager {
// This is created lazily since it can connect via binder.
bool EnsureImageManager();
- // Ensure we're connected to snapuserd.
- bool EnsureSnapuserdConnected();
-
- // Helpers for first-stage init.
- const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
+ // Helper for first-stage init.
+ bool ForceLocalImageManager();
- // Helper functions for tests.
+ // Helper function for tests.
IImageManager* image_manager() const { return images_.get(); }
- void set_use_first_stage_snapuserd(bool value) { use_first_stage_snapuserd_ = value; }
// Since libsnapshot is included into multiple processes, we flock() our
// files for simple synchronization. LockedFile is a helper to assist with
@@ -469,7 +337,7 @@ class SnapshotManager final : public ISnapshotManager {
//
// All sizes are specified in bytes, and the device, snapshot, COW partition and COW file sizes
// must be a multiple of the sector size (512 bytes).
- bool CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_creator, SnapshotStatus* status);
+ bool CreateSnapshot(LockedFile* lock, SnapshotStatus* status);
// |name| should be the base partition name (e.g. "system_a"). Create the
// backing COW image using the size previously passed to CreateSnapshot().
@@ -486,15 +354,6 @@ class SnapshotManager final : public ISnapshotManager {
const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
std::string* dev_path);
- // Create a dm-user device for a given snapshot.
- bool MapDmUserCow(LockedFile* lock, const std::string& name, const std::string& cow_file,
- const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
- std::string* path);
-
- // Map the source device used for dm-user.
- bool MapSourceDevice(LockedFile* lock, const std::string& name,
- const std::chrono::milliseconds& timeout_ms, std::string* path);
-
// Map a COW image that was previous created with CreateCowImage.
std::optional<std::string> MapCowImage(const std::string& name,
const std::chrono::milliseconds& timeout_ms);
@@ -509,15 +368,11 @@ class SnapshotManager final : public ISnapshotManager {
// Unmap a COW image device previously mapped with MapCowImage().
bool UnmapCowImage(const std::string& name);
- // Unmap a COW and remove it from a MetadataBuilder.
- void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);
-
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
// List the known snapshot names.
- bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
- const std::string& suffix = "");
+ bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
// Check for a cancelled or rolled back merge, returning true if such a
// condition was detected and handled.
@@ -548,8 +403,7 @@ class SnapshotManager final : public ISnapshotManager {
// Interact with /metadata/ota/state.
UpdateState ReadUpdateState(LockedFile* file);
SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);
- bool WriteUpdateState(LockedFile* file, UpdateState state,
- MergeFailureCode failure_code = MergeFailureCode::Ok);
+ bool WriteUpdateState(LockedFile* file, UpdateState state);
bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);
std::string GetStateFilePath() const;
@@ -558,13 +412,11 @@ class SnapshotManager final : public ISnapshotManager {
std::string GetMergeStateFilePath() const;
// Helpers for merging.
- MergeFailureCode MergeSecondPhaseSnapshots(LockedFile* lock);
- MergeFailureCode SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
- MergeFailureCode RewriteSnapshotDeviceTable(const std::string& dm_name);
+ bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
+ bool RewriteSnapshotDeviceTable(const std::string& dm_name);
bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
void AcknowledgeMergeSuccess(LockedFile* lock);
- void AcknowledgeMergeFailure(MergeFailureCode failure_code);
- MergePhase DecideMergePhase(const SnapshotStatus& status);
+ void AcknowledgeMergeFailure();
std::unique_ptr<LpMetadata> ReadCurrentMetadata();
enum class MetadataPartitionState {
@@ -590,25 +442,14 @@ class SnapshotManager final : public ISnapshotManager {
const SnapshotStatus& status);
bool CollapseSnapshotDevice(const std::string& name, const SnapshotStatus& status);
- struct MergeResult {
- explicit MergeResult(UpdateState state,
- MergeFailureCode failure_code = MergeFailureCode::Ok)
- : state(state), failure_code(failure_code) {}
- UpdateState state;
- MergeFailureCode failure_code;
- };
-
// Only the following UpdateStates are used here:
// UpdateState::Merging
// UpdateState::MergeCompleted
// UpdateState::MergeFailed
// UpdateState::MergeNeedsReboot
- MergeResult CheckMergeState(const std::function<bool()>& before_cancel);
- MergeResult CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
- MergeResult CheckTargetMergeState(LockedFile* lock, const std::string& name,
- const SnapshotUpdateStatus& update_status);
- MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
- const SnapshotStatus& update_status);
+ UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
+ UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
+ UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
// Interact with status files under /metadata/ota/snapshots.
bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
@@ -618,46 +459,16 @@ class SnapshotManager final : public ISnapshotManager {
std::string GetSnapshotBootIndicatorPath();
std::string GetRollbackIndicatorPath();
std::string GetForwardMergeIndicatorPath();
- std::string GetOldPartitionMetadataPath();
-
- const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
-
- bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,
- const std::chrono::milliseconds& timeout_ms);
-
- // Reason for calling MapPartitionWithSnapshot.
- enum class SnapshotContext {
- // For writing or verification (during update_engine).
- Update,
-
- // For mounting a full readable device.
- Mount,
- };
-
- struct SnapshotPaths {
- // Target/base device (eg system_b), always present.
- std::string target_device;
-
- // COW name (eg system_cow). Not present if no COW is needed.
- std::string cow_device_name;
-
- // dm-snapshot instance. Not present in Update mode for VABC.
- std::string snapshot_device;
- };
- // Helpers for OpenSnapshotWriter.
- std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths);
- std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths);
+ // Return the name of the device holding the "snapshot" or "snapshot-merge"
+ // target. This may not be the final device presented via MapSnapshot(), if
+ // for example there is a linear segment.
+ std::string GetSnapshotDeviceName(const std::string& snapshot_name,
+ const SnapshotStatus& status);
// Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
- SnapshotContext context, SnapshotPaths* paths);
+ std::string* path);
// Map the COW devices, including the partition in super and the images.
// |params|:
@@ -677,9 +488,6 @@ class SnapshotManager final : public ISnapshotManager {
// The reverse of MapPartitionWithSnapshot.
bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
- // Unmap a dm-user device through snapuserd.
- bool UnmapDmUserDevice(const std::string& snapshot_name);
-
// If there isn't a previous update, return true. |needs_merge| is set to false.
// If there is a previous update but the device has not boot into it, tries to cancel the
// update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
@@ -701,30 +509,25 @@ class SnapshotManager final : public ISnapshotManager {
const LpMetadata* exported_target_metadata, const std::string& target_suffix,
const std::map<std::string, SnapshotStatus>& all_snapshot_status);
- // Implementation of UnmapAllSnapshots(), with the lock provided.
- bool UnmapAllSnapshots(LockedFile* lock);
-
// Unmap all partitions that were mapped by CreateLogicalAndSnapshotPartitions.
// This should only be called in recovery.
- bool UnmapAllPartitionsInRecovery();
+ bool UnmapAllPartitions();
- // Check no snapshot overflows. Note that this returns false negatives if the snapshot
- // overflows, then is remapped and not written afterwards.
+ // Sanity check no snapshot overflows. Note that this returns false negatives if the snapshot
+ // overflows, then is remapped and not written afterwards. Hence, the function may only serve
+ // as a sanity check.
bool EnsureNoOverflowSnapshot(LockedFile* lock);
enum class Slot { Unknown, Source, Target };
friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);
Slot GetCurrentSlot();
- // Return the suffix we expect snapshots to have.
- std::string GetSnapshotSlotSuffix();
-
std::string ReadUpdateSourceSlotSuffix();
// Helper for RemoveAllSnapshots.
// Check whether |name| should be deleted as a snapshot name.
- bool ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status, Slot current_slot,
- const std::string& name);
+ bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
+ Slot current_slot, const std::string& name);
// Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,
// allow forward merge on FDR.
@@ -734,53 +537,19 @@ class SnapshotManager final : public ISnapshotManager {
// Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
// if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
// necessary.
- UpdateState ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
- const std::function<bool()>& callback);
+ bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+ const std::function<bool()>& callback);
// Return device string of a mapped image, or if it is not available, the mapped image path.
bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
std::string* device_string_or_mapped_path);
- // Same as above, but for paths only (no major:minor device strings).
- bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
-
- // Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
- // This is needed for any code that uses device-mapper path in first-stage init. If
- // |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
- // returns true.
- bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
-
- enum class InitTransition { SELINUX_DETACH, SECOND_STAGE };
-
- // Initiate the transition from first-stage to second-stage snapuserd. This
- // process involves re-creating the dm-user table entries for each device,
- // so that they connect to the new daemon. Once all new tables have been
- // activated, we ask the first-stage daemon to cleanly exit.
- //
- // If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will
- // be populated with a list of snapuserd arguments to pass to execve(). It
- // is otherwise ignored.
- bool PerformInitTransition(InitTransition transition,
- std::vector<std::string>* snapuserd_argv = nullptr);
-
- SnapuserdClient* snapuserd_client() const { return snapuserd_client_.get(); }
-
- // Helper of UpdateUsesCompression
- bool UpdateUsesCompression(LockedFile* lock);
-
- // Wrapper around libdm, with diagnostics.
- bool DeleteDeviceIfExists(const std::string& name,
- const std::chrono::milliseconds& timeout_ms = {});
-
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
- bool use_first_stage_snapuserd_ = false;
+ bool has_local_image_manager_ = false;
bool in_factory_data_reset_ = false;
- std::function<bool(const std::string&)> uevent_regen_callback_;
- std::unique_ptr<SnapuserdClient> snapuserd_client_;
- std::unique_ptr<LpMetadata> old_partition_metadata_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 8c2fec726..bdc3ea325 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -23,26 +23,16 @@
namespace android {
namespace snapshot {
-class ISnapshotMergeStats {
+class SnapshotMergeStats {
public:
- virtual ~ISnapshotMergeStats() = default;
+ // Not thread safe.
+ static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+
// Called when merge starts or resumes.
- virtual bool Start() = 0;
- virtual void set_state(android::snapshot::UpdateState state, bool using_compression) = 0;
- virtual void set_cow_file_size(uint64_t cow_file_size) = 0;
- virtual void set_total_cow_size_bytes(uint64_t bytes) = 0;
- virtual void set_estimated_cow_size_bytes(uint64_t bytes) = 0;
- virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
- virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
- virtual void set_merge_failure_code(MergeFailureCode code) = 0;
- virtual void set_source_build_fingerprint(const std::string& fingerprint) = 0;
- virtual uint64_t cow_file_size() = 0;
- virtual uint64_t total_cow_size_bytes() = 0;
- virtual uint64_t estimated_cow_size_bytes() = 0;
- virtual uint32_t boot_complete_time_ms() = 0;
- virtual uint32_t boot_complete_to_merge_start_time_ms() = 0;
- virtual MergeFailureCode merge_failure_code() = 0;
- virtual std::string source_build_fingerprint() = 0;
+ bool Start();
+ void set_state(android::snapshot::UpdateState state);
+ virtual void set_cow_file_size(uint64_t cow_file_size);
+ virtual uint64_t cow_file_size();
// Called when merge ends. Properly clean up permanent storage.
class Result {
@@ -52,41 +42,13 @@ class ISnapshotMergeStats {
// Time between successful Start() / Resume() to Finish().
virtual std::chrono::steady_clock::duration merge_time() const = 0;
};
- // Return nullptr if any failure.
- virtual std::unique_ptr<Result> Finish() = 0;
-
- // Write out the current state. This should be called when data might be lost that
- // cannot be recovered (eg the COW sizes).
- virtual bool WriteState() = 0;
-};
-
-class SnapshotMergeStats : public ISnapshotMergeStats {
- public:
- // Not thread safe.
- static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
-
- // ISnapshotMergeStats overrides
- bool Start() override;
- void set_state(android::snapshot::UpdateState state, bool using_compression) override;
- void set_cow_file_size(uint64_t cow_file_size) override;
- uint64_t cow_file_size() override;
- void set_total_cow_size_bytes(uint64_t bytes) override;
- void set_estimated_cow_size_bytes(uint64_t bytes) override;
- uint64_t total_cow_size_bytes() override;
- uint64_t estimated_cow_size_bytes() override;
- void set_boot_complete_time_ms(uint32_t ms) override;
- uint32_t boot_complete_time_ms() override;
- void set_boot_complete_to_merge_start_time_ms(uint32_t ms) override;
- uint32_t boot_complete_to_merge_start_time_ms() override;
- void set_merge_failure_code(MergeFailureCode code) override;
- MergeFailureCode merge_failure_code() override;
- void set_source_build_fingerprint(const std::string& fingerprint) override;
- std::string source_build_fingerprint() override;
- std::unique_ptr<Result> Finish() override;
- bool WriteState() override;
+ std::unique_ptr<Result> Finish();
private:
+ virtual ~SnapshotMergeStats() {}
+
bool ReadState();
+ bool WriteState();
bool DeleteState();
SnapshotMergeStats(const std::string& path);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
deleted file mode 100644
index 74b78c59b..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <libsnapshot/snapshot.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android::snapshot {
-
-class SnapshotManagerStub : public ISnapshotManager {
- public:
- // Create a stubbed snapshot manager. All calls into the stub fails.
- static std::unique_ptr<ISnapshotManager> New();
-
- // ISnapshotManager overrides.
- bool BeginUpdate() override;
- bool CancelUpdate() override;
- bool FinishedSnapshotWrites(bool wipe) override;
- void UpdateCowStats(ISnapshotMergeStats* stats) override;
- MergeFailureCode ReadMergeFailureCode() override;
- bool InitiateMerge() override;
- UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
- const std::function<bool()>& before_cancel = {}) override;
- UpdateState GetUpdateState(double* progress = nullptr) override;
- bool UpdateUsesCompression() override;
- Return CreateUpdateSnapshots(
- const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
- bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
- std::string* snapshot_path) override;
- std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) override;
- bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
- bool NeedSnapshotsInFirstStageMount() override;
- bool CreateLogicalAndSnapshotPartitions(
- const std::string& super_device,
- const std::chrono::milliseconds& timeout_ms = {}) override;
- bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
- bool FinishMergeInRecovery() override;
- CreateResult RecoveryCreateSnapshotDevices() override;
- CreateResult RecoveryCreateSnapshotDevices(
- const std::unique_ptr<AutoDevice>& metadata_device) override;
- bool Dump(std::ostream& os) override;
- std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
- ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
- bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
- bool UnmapAllSnapshots() override;
- std::string ReadSourceBuildFingerprint() override;
-};
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
deleted file mode 100644
index bf5ce8bb5..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <optional>
-
-#include <android-base/unique_fd.h>
-
-#include <libsnapshot/cow_writer.h>
-
-namespace chromeos_update_engine {
-class FileDescriptor;
-} // namespace chromeos_update_engine
-
-namespace android {
-namespace snapshot {
-
-class ISnapshotWriter : public ICowWriter {
- public:
- using FileDescriptor = chromeos_update_engine::FileDescriptor;
-
- explicit ISnapshotWriter(const CowOptions& options);
-
- // Set the source device. This is used for AddCopy() operations, if the
- // underlying writer needs the original bytes (for example if backed by
- // dm-snapshot or if writing directly to an unsnapshotted region). The
- // device is only opened on the first operation that requires it.
- void SetSourceDevice(const std::string& source_device);
-
- // Open the writer in write mode (no append).
- virtual bool Initialize() = 0;
-
- // Open the writer in append mode, with the last label to resume
- // from. See CowWriter::InitializeAppend.
- virtual bool InitializeAppend(uint64_t label) = 0;
-
- virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
-
- protected:
- android::base::borrowed_fd GetSourceFd();
-
- std::optional<std::string> source_device_;
-
- private:
- android::base::unique_fd source_fd_;
-};
-
-// Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter : public ISnapshotWriter {
- public:
- CompressedSnapshotWriter(const CowOptions& options);
-
- // Sets the COW device; this is required.
- bool SetCowDevice(android::base::unique_fd&& cow_device);
-
- bool Initialize() override;
- bool InitializeAppend(uint64_t label) override;
- bool Finalize() override;
- uint64_t GetCowSize() override;
- std::unique_ptr<FileDescriptor> OpenReader() override;
-
- protected:
- bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
- bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool EmitLabel(uint64_t label) override;
-
- private:
- android::base::unique_fd cow_device_;
-
- std::unique_ptr<CowWriter> cow_;
-};
-
-// Write directly to a dm-snapshot device.
-class OnlineKernelSnapshotWriter : public ISnapshotWriter {
- public:
- OnlineKernelSnapshotWriter(const CowOptions& options);
-
- // Set the device used for all writes.
- void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
-
- bool Initialize() override { return true; }
- bool InitializeAppend(uint64_t) override { return true; }
-
- bool Finalize() override;
- uint64_t GetCowSize() override { return cow_size_; }
- std::unique_ptr<FileDescriptor> OpenReader() override;
-
- protected:
- bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
- bool EmitLabel(uint64_t label) override;
-
- private:
- android::base::unique_fd snapshot_fd_;
- uint64_t cow_size_ = 0;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
deleted file mode 100644
index 280e85730..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <unistd.h>
-
-#include <chrono>
-#include <cstring>
-#include <iostream>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace snapshot {
-
-static constexpr uint32_t PACKET_SIZE = 512;
-
-static constexpr char kSnapuserdSocket[] = "snapuserd";
-
-// Ensure that the second-stage daemon for snapuserd is running.
-bool EnsureSnapuserdStarted();
-
-class SnapuserdClient {
- private:
- android::base::unique_fd sockfd_;
-
- bool Sendmsg(const std::string& msg);
- std::string Receivemsg();
-
- bool ValidateConnection();
-
- public:
- explicit SnapuserdClient(android::base::unique_fd&& sockfd);
-
- static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
- std::chrono::milliseconds timeout_ms);
-
- bool StopSnapuserd();
-
- // Initializing a snapuserd handler is a three-step process:
- //
- // 1. Client invokes InitDmUserCow. This creates the snapuserd handler and validates the
- // COW. The number of sectors required for the dm-user target is returned.
- // 2. Client creates the device-mapper device with the dm-user target.
- // 3. Client calls AttachControlDevice.
- //
- // The misc_name must be the "misc_name" given to dm-user in step 2.
- //
- uint64_t InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device);
- bool AttachDmUser(const std::string& misc_name);
-
- // Wait for snapuserd to disassociate with a dm-user control device. This
- // must ONLY be called if the control device has already been deleted.
- bool WaitForDeviceDelete(const std::string& control_device);
-
- void CloseConnection() { sockfd_ = {}; }
-
- // Detach snapuserd. This shuts down the listener socket, and will cause
- // snapuserd to gracefully exit once all handler threads have terminated.
- // This should only be used on first-stage instances of snapuserd.
- bool DetachSnapuserd();
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
deleted file mode 100644
index 6bb7a394e..000000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-namespace android {
-namespace snapshot {
-
-#define DM_USER_REQ_MAP_READ 0
-#define DM_USER_REQ_MAP_WRITE 1
-
-#define DM_USER_RESP_SUCCESS 0
-#define DM_USER_RESP_ERROR 1
-#define DM_USER_RESP_UNSUPPORTED 2
-
-// Kernel COW header fields
-static constexpr uint32_t SNAP_MAGIC = 0x70416e53;
-
-static constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;
-
-static constexpr uint32_t NUM_SNAPSHOT_HDR_CHUNKS = 1;
-
-static constexpr uint32_t SNAPSHOT_VALID = 1;
-
-/*
- * The basic unit of block I/O is a sector. It is used in a number of contexts
- * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9
- * bytes. Variables of type sector_t represent an offset or size that is a
- * multiple of 512 bytes. Hence these two constants.
- */
-static constexpr uint32_t SECTOR_SHIFT = 9;
-
-typedef __u64 sector_t;
-typedef sector_t chunk_t;
-
-static constexpr uint32_t CHUNK_SIZE = 8;
-static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
-
-#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
-
-// This structure represents the kernel COW header.
-// All the below fields should be in Little Endian format.
-struct disk_header {
- uint32_t magic;
-
- /*
- * Is this snapshot valid. There is no way of recovering
- * an invalid snapshot.
- */
- uint32_t valid;
-
- /*
- * Simple, incrementing version. no backward
- * compatibility.
- */
- uint32_t version;
-
- /* In sectors */
- uint32_t chunk_size;
-} __packed;
-
-// A disk exception is a mapping of old_chunk to new_chunk
-// old_chunk is the chunk ID of a dm-snapshot device.
-// new_chunk is the chunk ID of the COW device.
-struct disk_exception {
- uint64_t old_chunk;
- uint64_t new_chunk;
-} __packed;
-
-// Control structures to communicate with dm-user
-// It comprises of header and a payload
-struct dm_user_header {
- __u64 seq;
- __u64 type;
- __u64 flags;
- __u64 sector;
- __u64 len;
-} __attribute__((packed));
-
-struct dm_user_payload {
- __u8 buf[];
-};
-
-// Message comprising both header and payload
-struct dm_user_message {
- struct dm_user_header header;
- struct dm_user_payload payload;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 4e7ccf145..8e369b052 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -51,8 +51,8 @@ using namespace std::string_literals;
extern std::unique_ptr<SnapshotManager> sm;
extern class TestDeviceInfo* test_device;
extern std::string fake_super;
-static constexpr uint64_t kSuperSize = 32_MiB + 4_KiB;
-static constexpr uint64_t kGroupSize = 32_MiB;
+static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
+static constexpr uint64_t kGroupSize = 16_MiB;
// Redirect requests for "super" to our fake super partition.
class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
@@ -77,6 +77,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
: TestDeviceInfo(fake_super) {
set_slot_suffix(slot_suffix);
}
+ std::string GetGsidDir() const override { return "ota/test"s; }
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
std::string GetSlotSuffix() const override { return slot_suffix_; }
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
@@ -94,11 +95,6 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
unbootable_slots_.insert(slot);
return true;
}
- bool IsTestDevice() const override { return true; }
- bool IsFirstStageInit() const override { return first_stage_init_; }
- std::unique_ptr<IImageManager> OpenImageManager() const override {
- return IDeviceInfo::OpenImageManager("ota/test");
- }
bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
@@ -107,7 +103,6 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
opener_ = std::make_unique<TestPartitionOpener>(path);
}
void set_recovery(bool value) { recovery_ = value; }
- void set_first_stage_init(bool value) { first_stage_init_ = value; }
MergeStatus merge_status() const { return merge_status_; }
private:
@@ -115,7 +110,6 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
std::unique_ptr<TestPartitionOpener> opener_;
MergeStatus merge_status_;
bool recovery_ = false;
- bool first_stage_init_ = false;
std::unordered_set<uint32_t> unbootable_slots_;
};
@@ -150,8 +144,6 @@ void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::stri
// Expect space of |path| is multiple of 4K.
bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
std::string* hash = nullptr);
-bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
-std::string HashSnapshot(ISnapshotWriter* writer);
std::optional<std::string> GetHash(const std::string& path);
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
deleted file mode 100644
index 4a84fba0d..000000000
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-#include <stdio.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <libsnapshot/cow_reader.h>
-
-namespace android {
-namespace snapshot {
-
-void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
- unsigned int, const char* message) {
- if (severity == android::base::ERROR) {
- fprintf(stderr, "%s\n", message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-static void usage(void) {
- LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
- LOG(ERROR) << "\t -s Run Silent";
- LOG(ERROR) << "\t -d Attempt to decompress";
- LOG(ERROR) << "\t -b Show data for failed decompress\n";
-}
-
-// Sink that always appends to the end of a string.
-class StringSink : public IByteSink {
- public:
- void* GetBuffer(size_t requested, size_t* actual) override {
- size_t old_size = stream_.size();
- stream_.resize(old_size + requested, '\0');
- *actual = requested;
- return stream_.data() + old_size;
- }
- bool ReturnData(void*, size_t) override { return true; }
- void Reset() { stream_.clear(); }
-
- std::string& stream() { return stream_; }
-
- private:
- std::string stream_;
-};
-
-static void ShowBad(CowReader& reader, const struct CowOperation& op) {
- size_t count;
- auto buffer = std::make_unique<uint8_t[]>(op.data_length);
-
- if (!reader.GetRawBytes(op.source, buffer.get(), op.data_length, &count)) {
- std::cerr << "Failed to read at all!\n";
- } else {
- std::cout << "The Block data is:\n";
- for (int i = 0; i < op.data_length; i++) {
- std::cout << std::hex << (int)buffer[i];
- }
- std::cout << std::dec << "\n\n";
- if (op.data_length >= sizeof(CowOperation)) {
- std::cout << "The start, as an op, would be " << *(CowOperation*)buffer.get() << "\n";
- }
- }
-}
-
-static bool Inspect(const std::string& path, bool silent, bool decompress, bool show_bad) {
- android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << path;
- return false;
- }
-
- CowReader reader;
- if (!reader.Parse(fd)) {
- LOG(ERROR) << "parse failed: " << path;
- return false;
- }
-
- CowHeader header;
- if (!reader.GetHeader(&header)) {
- LOG(ERROR) << "could not get header: " << path;
- return false;
- }
- CowFooter footer;
- bool has_footer = false;
- if (reader.GetFooter(&footer)) has_footer = true;
-
- if (!silent) {
- std::cout << "Major version: " << header.major_version << "\n";
- std::cout << "Minor version: " << header.minor_version << "\n";
- std::cout << "Header size: " << header.header_size << "\n";
- std::cout << "Footer size: " << header.footer_size << "\n";
- std::cout << "Block size: " << header.block_size << "\n";
- std::cout << "\n";
- if (has_footer) {
- std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
- std::cout << "Number of Ops: " << footer.op.num_ops << "\n";
- std::cout << "\n";
- }
- }
-
- auto iter = reader.GetOpIter();
- StringSink sink;
- bool success = true;
- while (!iter->Done()) {
- const CowOperation& op = iter->Get();
-
- if (!silent) std::cout << op << "\n";
-
- if (decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
- if (!reader.ReadData(op, &sink)) {
- std::cerr << "Failed to decompress for :" << op << "\n";
- success = false;
- if (show_bad) ShowBad(reader, op);
- }
- sink.Reset();
- }
-
- iter->Next();
- }
-
- return success;
-}
-
-} // namespace snapshot
-} // namespace android
-
-int main(int argc, char** argv) {
- int ch;
- bool silent = false;
- bool decompress = false;
- bool show_bad = false;
- while ((ch = getopt(argc, argv, "sdb")) != -1) {
- switch (ch) {
- case 's':
- silent = true;
- break;
- case 'd':
- decompress = true;
- break;
- case 'b':
- show_bad = true;
- break;
- default:
- android::snapshot::usage();
- }
- }
- android::base::InitLogging(argv, android::snapshot::MyLogger);
-
- if (argc < optind + 1) {
- android::snapshot::usage();
- return 1;
- }
-
- if (!android::snapshot::Inspect(argv[optind], silent, decompress, show_bad)) {
- return 1;
- }
- return 0;
-}
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
deleted file mode 100644
index 6a5754de1..000000000
--- a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
+++ /dev/null
@@ -1,692 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <limits>
-#include <string>
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <bsdiff/bspatch.h>
-#include <bzlib.h>
-#include <gflags/gflags.h>
-#include <libsnapshot/cow_writer.h>
-#include <puffin/puffpatch.h>
-#include <sparse/sparse.h>
-#include <update_engine/update_metadata.pb.h>
-#include <xz.h>
-#include <ziparchive/zip_archive.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::Extent;
-using chromeos_update_engine::InstallOperation;
-using chromeos_update_engine::PartitionUpdate;
-
-static constexpr uint64_t kBlockSize = 4096;
-
-DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
-DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
-DEFINE_uint32(cluster_ops, 0, "Number of Cow Ops per cluster (0 or >1)");
-
-void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
- unsigned int, const char* message) {
- if (severity == android::base::ERROR) {
- fprintf(stderr, "%s\n", message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-uint64_t ToLittleEndian(uint64_t value) {
- union {
- uint64_t u64;
- char bytes[8];
- } packed;
- packed.u64 = value;
- std::swap(packed.bytes[0], packed.bytes[7]);
- std::swap(packed.bytes[1], packed.bytes[6]);
- std::swap(packed.bytes[2], packed.bytes[5]);
- std::swap(packed.bytes[3], packed.bytes[4]);
- return packed.u64;
-}
-
-class PayloadConverter final {
- public:
- PayloadConverter(const std::string& in_file, const std::string& out_dir)
- : in_file_(in_file), out_dir_(out_dir), source_tf_zip_(nullptr, &CloseArchive) {}
-
- bool Run();
-
- private:
- bool OpenPayload();
- bool OpenSourceTargetFiles();
- bool ProcessPartition(const PartitionUpdate& update);
- bool ProcessOperation(const InstallOperation& op);
- bool ProcessZero(const InstallOperation& op);
- bool ProcessCopy(const InstallOperation& op);
- bool ProcessReplace(const InstallOperation& op);
- bool ProcessDiff(const InstallOperation& op);
- borrowed_fd OpenSourceImage();
-
- std::string in_file_;
- std::string out_dir_;
- unique_fd in_fd_;
- uint64_t payload_offset_ = 0;
- DeltaArchiveManifest manifest_;
- std::unordered_set<std::string> dap_;
- unique_fd source_tf_fd_;
- std::unique_ptr<ZipArchive, decltype(&CloseArchive)> source_tf_zip_;
-
- // Updated during ProcessPartition().
- std::string partition_name_;
- std::unique_ptr<CowWriter> writer_;
- unique_fd source_image_;
-};
-
-bool PayloadConverter::Run() {
- if (!OpenPayload()) {
- return false;
- }
-
- if (manifest_.has_dynamic_partition_metadata()) {
- const auto& dpm = manifest_.dynamic_partition_metadata();
- for (const auto& group : dpm.groups()) {
- for (const auto& partition : group.partition_names()) {
- dap_.emplace(partition);
- }
- }
- }
-
- if (dap_.empty()) {
- LOG(ERROR) << "No dynamic partitions found.";
- return false;
- }
-
- if (!OpenSourceTargetFiles()) {
- return false;
- }
-
- for (const auto& update : manifest_.partitions()) {
- if (!ProcessPartition(update)) {
- return false;
- }
- writer_ = nullptr;
- source_image_.reset();
- }
- return true;
-}
-
-bool PayloadConverter::OpenSourceTargetFiles() {
- if (FLAGS_source_tf.empty()) {
- return true;
- }
-
- source_tf_fd_.reset(open(FLAGS_source_tf.c_str(), O_RDONLY));
- if (source_tf_fd_ < 0) {
- LOG(ERROR) << "open failed: " << FLAGS_source_tf;
- return false;
- }
-
- struct stat s;
- if (fstat(source_tf_fd_.get(), &s) < 0) {
- LOG(ERROR) << "fstat failed: " << FLAGS_source_tf;
- return false;
- }
- if (S_ISDIR(s.st_mode)) {
- return true;
- }
-
- // Otherwise, assume it's a zip file.
- ZipArchiveHandle handle;
- if (OpenArchiveFd(source_tf_fd_.get(), FLAGS_source_tf.c_str(), &handle, false)) {
- LOG(ERROR) << "Could not open " << FLAGS_source_tf << " as a zip archive.";
- return false;
- }
- source_tf_zip_.reset(handle);
- return true;
-}
-
-bool PayloadConverter::ProcessPartition(const PartitionUpdate& update) {
- auto partition_name = update.partition_name();
- if (dap_.find(partition_name) == dap_.end()) {
- // Skip non-DAP partitions.
- return true;
- }
-
- auto path = out_dir_ + "/" + partition_name + ".cow";
- unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << path;
- return false;
- }
-
- CowOptions options;
- options.block_size = kBlockSize;
- options.compression = FLAGS_compression;
- options.cluster_ops = FLAGS_cluster_ops;
-
- writer_ = std::make_unique<CowWriter>(options);
- if (!writer_->Initialize(std::move(fd))) {
- LOG(ERROR) << "Unable to initialize COW writer";
- return false;
- }
-
- partition_name_ = partition_name;
-
- for (const auto& op : update.operations()) {
- if (!ProcessOperation(op)) {
- return false;
- }
- }
-
- if (!writer_->Finalize()) {
- LOG(ERROR) << "Unable to finalize COW for " << partition_name;
- return false;
- }
- return true;
-}
-
-bool PayloadConverter::ProcessOperation(const InstallOperation& op) {
- switch (op.type()) {
- case InstallOperation::SOURCE_COPY:
- return ProcessCopy(op);
- case InstallOperation::BROTLI_BSDIFF:
- case InstallOperation::PUFFDIFF:
- return ProcessDiff(op);
- case InstallOperation::REPLACE:
- case InstallOperation::REPLACE_XZ:
- case InstallOperation::REPLACE_BZ:
- return ProcessReplace(op);
- case InstallOperation::ZERO:
- return ProcessZero(op);
- default:
- LOG(ERROR) << "Unsupported op: " << (int)op.type();
- return false;
- }
- return true;
-}
-
-bool PayloadConverter::ProcessZero(const InstallOperation& op) {
- for (const auto& extent : op.dst_extents()) {
- if (!writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks())) {
- LOG(ERROR) << "Could not add zero operation";
- return false;
- }
- }
- return true;
-}
-
-template <typename T>
-static uint64_t SizeOfAllExtents(const T& extents) {
- uint64_t total = 0;
- for (const auto& extent : extents) {
- total += extent.num_blocks() * kBlockSize;
- }
- return total;
-}
-
-class PuffInputStream final : public puffin::StreamInterface {
- public:
- PuffInputStream(uint8_t* buffer, size_t length) : buffer_(buffer), length_(length), pos_(0) {}
-
- bool GetSize(uint64_t* size) const override {
- *size = length_;
- return true;
- }
- bool GetOffset(uint64_t* offset) const override {
- *offset = pos_;
- return true;
- }
- bool Seek(uint64_t offset) override {
- if (offset > length_) return false;
- pos_ = offset;
- return true;
- }
- bool Read(void* buffer, size_t length) override {
- if (length_ - pos_ < length) return false;
- memcpy(buffer, buffer_ + pos_, length);
- pos_ += length;
- return true;
- }
- bool Write(const void*, size_t) override { return false; }
- bool Close() override { return true; }
-
- private:
- uint8_t* buffer_;
- size_t length_;
- size_t pos_;
-};
-
-class PuffOutputStream final : public puffin::StreamInterface {
- public:
- PuffOutputStream(std::vector<uint8_t>& stream) : stream_(stream), pos_(0) {}
-
- bool GetSize(uint64_t* size) const override {
- *size = stream_.size();
- return true;
- }
- bool GetOffset(uint64_t* offset) const override {
- *offset = pos_;
- return true;
- }
- bool Seek(uint64_t offset) override {
- if (offset > stream_.size()) {
- return false;
- }
- pos_ = offset;
- return true;
- }
- bool Read(void* buffer, size_t length) override {
- if (stream_.size() - pos_ < length) {
- return false;
- }
- memcpy(buffer, &stream_[0] + pos_, length);
- pos_ += length;
- return true;
- }
- bool Write(const void* buffer, size_t length) override {
- auto remaining = stream_.size() - pos_;
- if (remaining < length) {
- stream_.resize(stream_.size() + (length - remaining));
- }
- memcpy(&stream_[0] + pos_, buffer, length);
- pos_ += length;
- return true;
- }
- bool Close() override { return true; }
-
- private:
- std::vector<uint8_t>& stream_;
- size_t pos_;
-};
-
-bool PayloadConverter::ProcessDiff(const InstallOperation& op) {
- auto source_image = OpenSourceImage();
- if (source_image < 0) {
- return false;
- }
-
- uint64_t src_length = SizeOfAllExtents(op.src_extents());
- auto src = std::make_unique<uint8_t[]>(src_length);
- size_t src_pos = 0;
-
- // Read source bytes.
- for (const auto& extent : op.src_extents()) {
- uint64_t offset = extent.start_block() * kBlockSize;
- if (lseek(source_image.get(), offset, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek source image failed";
- return false;
- }
-
- uint64_t size = extent.num_blocks() * kBlockSize;
- CHECK(src_length - src_pos >= size);
- if (!android::base::ReadFully(source_image, src.get() + src_pos, size)) {
- PLOG(ERROR) << "read source image failed";
- return false;
- }
- src_pos += size;
- }
- CHECK(src_pos == src_length);
-
- // Read patch bytes.
- auto patch = std::make_unique<uint8_t[]>(op.data_length());
- if (lseek(in_fd_.get(), payload_offset_ + op.data_offset(), SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek payload failed";
- return false;
- }
- if (!android::base::ReadFully(in_fd_, patch.get(), op.data_length())) {
- PLOG(ERROR) << "read payload failed";
- return false;
- }
-
- std::vector<uint8_t> dest(SizeOfAllExtents(op.dst_extents()));
-
- // Apply the diff.
- if (op.type() == InstallOperation::BROTLI_BSDIFF) {
- size_t dest_pos = 0;
- auto sink = [&](const uint8_t* data, size_t length) -> size_t {
- CHECK(dest.size() - dest_pos >= length);
- memcpy(&dest[dest_pos], data, length);
- dest_pos += length;
- return length;
- };
- if (int rv = bsdiff::bspatch(src.get(), src_pos, patch.get(), op.data_length(), sink)) {
- LOG(ERROR) << "bspatch failed, error code " << rv;
- return false;
- }
- } else if (op.type() == InstallOperation::PUFFDIFF) {
- auto src_stream = std::make_unique<PuffInputStream>(src.get(), src_length);
- auto dest_stream = std::make_unique<PuffOutputStream>(dest);
- bool ok = PuffPatch(std::move(src_stream), std::move(dest_stream), patch.get(),
- op.data_length());
- if (!ok) {
- LOG(ERROR) << "puffdiff operation failed to apply";
- return false;
- }
- } else {
- LOG(ERROR) << "unsupported diff operation: " << op.type();
- return false;
- }
-
- // Write the final blocks to the COW.
- size_t dest_pos = 0;
- for (const auto& extent : op.dst_extents()) {
- uint64_t size = extent.num_blocks() * kBlockSize;
- CHECK(dest.size() - dest_pos >= size);
-
- if (!writer_->AddRawBlocks(extent.start_block(), &dest[dest_pos], size)) {
- return false;
- }
- dest_pos += size;
- }
- return true;
-}
-
-borrowed_fd PayloadConverter::OpenSourceImage() {
- if (source_image_ >= 0) {
- return source_image_;
- }
-
- unique_fd unzip_fd;
-
- auto local_path = "IMAGES/" + partition_name_ + ".img";
- if (source_tf_zip_) {
- {
- TemporaryFile tmp;
- if (tmp.fd < 0) {
- PLOG(ERROR) << "mkstemp failed";
- return -1;
- }
- unzip_fd.reset(tmp.release());
- }
-
- ZipEntry64 entry;
- if (FindEntry(source_tf_zip_.get(), local_path, &entry)) {
- LOG(ERROR) << "not found in archive: " << local_path;
- return -1;
- }
- if (ExtractEntryToFile(source_tf_zip_.get(), &entry, unzip_fd.get())) {
- LOG(ERROR) << "could not extract " << local_path;
- return -1;
- }
- if (lseek(unzip_fd.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return -1;
- }
- } else if (source_tf_fd_ >= 0) {
- unzip_fd.reset(openat(source_tf_fd_.get(), local_path.c_str(), O_RDONLY));
- if (unzip_fd < 0) {
- PLOG(ERROR) << "open failed: " << FLAGS_source_tf << "/" << local_path;
- return -1;
- }
- } else {
- LOG(ERROR) << "No source target files package was specified; need -source_tf";
- return -1;
- }
-
- std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
- sparse_file_import(unzip_fd.get(), false, false), &sparse_file_destroy);
- if (s) {
- TemporaryFile tmp;
- if (tmp.fd < 0) {
- PLOG(ERROR) << "mkstemp failed";
- return -1;
- }
- if (sparse_file_write(s.get(), tmp.fd, false, false, false) < 0) {
- LOG(ERROR) << "sparse_file_write failed";
- return -1;
- }
- source_image_.reset(tmp.release());
- } else {
- source_image_ = std::move(unzip_fd);
- }
- return source_image_;
-}
-
-template <typename ContainerType>
-class ExtentIter final {
- public:
- ExtentIter(const ContainerType& container)
- : iter_(container.cbegin()), end_(container.cend()), dst_index_(0) {}
-
- bool GetNext(uint64_t* block) {
- while (iter_ != end_) {
- if (dst_index_ < iter_->num_blocks()) {
- break;
- }
- iter_++;
- dst_index_ = 0;
- }
- if (iter_ == end_) {
- return false;
- }
- *block = iter_->start_block() + dst_index_;
- dst_index_++;
- return true;
- }
-
- private:
- typename ContainerType::const_iterator iter_;
- typename ContainerType::const_iterator end_;
- uint64_t dst_index_;
-};
-
-bool PayloadConverter::ProcessCopy(const InstallOperation& op) {
- ExtentIter dst_blocks(op.dst_extents());
-
- for (const auto& extent : op.src_extents()) {
- for (uint64_t i = 0; i < extent.num_blocks(); i++) {
- uint64_t src_block = extent.start_block() + i;
- uint64_t dst_block;
- if (!dst_blocks.GetNext(&dst_block)) {
- LOG(ERROR) << "SOURCE_COPY contained mismatching extents";
- return false;
- }
- if (src_block == dst_block) continue;
- if (!writer_->AddCopy(dst_block, src_block)) {
- LOG(ERROR) << "Could not add copy operation";
- return false;
- }
- }
- }
- return true;
-}
-
-bool PayloadConverter::ProcessReplace(const InstallOperation& op) {
- auto buffer_size = op.data_length();
- auto buffer = std::make_unique<char[]>(buffer_size);
- uint64_t offs = payload_offset_ + op.data_offset();
- if (lseek(in_fd_.get(), offs, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek " << offs << " failed";
- return false;
- }
- if (!android::base::ReadFully(in_fd_, buffer.get(), buffer_size)) {
- PLOG(ERROR) << "read " << buffer_size << " bytes from offset " << offs << "failed";
- return false;
- }
-
- uint64_t dst_size = 0;
- for (const auto& extent : op.dst_extents()) {
- dst_size += extent.num_blocks() * kBlockSize;
- }
-
- if (op.type() == InstallOperation::REPLACE_BZ) {
- auto tmp = std::make_unique<char[]>(dst_size);
-
- uint32_t actual_size;
- if (dst_size > std::numeric_limits<typeof(actual_size)>::max()) {
- LOG(ERROR) << "too many bytes to decompress: " << dst_size;
- return false;
- }
- actual_size = static_cast<uint32_t>(dst_size);
-
- auto rv = BZ2_bzBuffToBuffDecompress(tmp.get(), &actual_size, buffer.get(), buffer_size, 0,
- 0);
- if (rv) {
- LOG(ERROR) << "bz2 decompress failed: " << rv;
- return false;
- }
- if (actual_size != dst_size) {
- LOG(ERROR) << "bz2 returned " << actual_size << " bytes, expected " << dst_size;
- return false;
- }
- buffer = std::move(tmp);
- buffer_size = dst_size;
- } else if (op.type() == InstallOperation::REPLACE_XZ) {
- constexpr uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
-
- if (dst_size > std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "too many bytes to decompress: " << dst_size;
- return false;
- }
-
- std::unique_ptr<struct xz_dec, decltype(&xz_dec_end)> s(
- xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize), xz_dec_end);
- if (!s) {
- LOG(ERROR) << "xz_dec_init failed";
- return false;
- }
-
- auto tmp = std::make_unique<char[]>(dst_size);
-
- struct xz_buf args;
- args.in = reinterpret_cast<const uint8_t*>(buffer.get());
- args.in_pos = 0;
- args.in_size = buffer_size;
- args.out = reinterpret_cast<uint8_t*>(tmp.get());
- args.out_pos = 0;
- args.out_size = dst_size;
-
- auto rv = xz_dec_run(s.get(), &args);
- if (rv != XZ_STREAM_END) {
- LOG(ERROR) << "xz decompress failed: " << (int)rv;
- return false;
- }
- buffer = std::move(tmp);
- buffer_size = dst_size;
- }
-
- uint64_t buffer_pos = 0;
- for (const auto& extent : op.dst_extents()) {
- uint64_t extent_size = extent.num_blocks() * kBlockSize;
- if (buffer_size - buffer_pos < extent_size) {
- LOG(ERROR) << "replace op ran out of input buffer";
- return false;
- }
- if (!writer_->AddRawBlocks(extent.start_block(), buffer.get() + buffer_pos, extent_size)) {
- LOG(ERROR) << "failed to add raw blocks from replace op";
- return false;
- }
- buffer_pos += extent_size;
- }
- return true;
-}
-
-bool PayloadConverter::OpenPayload() {
- in_fd_.reset(open(in_file_.c_str(), O_RDONLY));
- if (in_fd_ < 0) {
- PLOG(ERROR) << "open " << in_file_;
- return false;
- }
-
- char magic[4];
- if (!android::base::ReadFully(in_fd_, magic, sizeof(magic))) {
- PLOG(ERROR) << "read magic";
- return false;
- }
- if (std::string(magic, sizeof(magic)) != "CrAU") {
- LOG(ERROR) << "Invalid magic in " << in_file_;
- return false;
- }
-
- uint64_t version;
- uint64_t manifest_size;
- uint32_t manifest_signature_size = 0;
- if (!android::base::ReadFully(in_fd_, &version, sizeof(version))) {
- PLOG(ERROR) << "read version";
- return false;
- }
- version = ToLittleEndian(version);
- if (version < 2) {
- LOG(ERROR) << "Only payload version 2 or higher is supported.";
- return false;
- }
-
- if (!android::base::ReadFully(in_fd_, &manifest_size, sizeof(manifest_size))) {
- PLOG(ERROR) << "read manifest_size";
- return false;
- }
- manifest_size = ToLittleEndian(manifest_size);
- if (!android::base::ReadFully(in_fd_, &manifest_signature_size,
- sizeof(manifest_signature_size))) {
- PLOG(ERROR) << "read manifest_signature_size";
- return false;
- }
- manifest_signature_size = ntohl(manifest_signature_size);
-
- auto manifest = std::make_unique<uint8_t[]>(manifest_size);
- if (!android::base::ReadFully(in_fd_, manifest.get(), manifest_size)) {
- PLOG(ERROR) << "read manifest";
- return false;
- }
-
- // Skip past manifest signature.
- auto offs = lseek(in_fd_, manifest_signature_size, SEEK_CUR);
- if (offs < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
- payload_offset_ = offs;
-
- if (!manifest_.ParseFromArray(manifest.get(), manifest_size)) {
- LOG(ERROR) << "could not parse manifest";
- return false;
- }
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
-
-int main(int argc, char** argv) {
- android::base::InitLogging(argv, android::snapshot::MyLogger);
- gflags::SetUsageMessage("Convert OTA payload to a Virtual A/B COW");
- int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, false);
-
- xz_crc32_init();
-
- if (argc - arg_start != 2) {
- std::cerr << "Usage: [options] <payload.bin> <out-dir>\n";
- return 1;
- }
-
- android::snapshot::PayloadConverter pc(argv[arg_start], argv[arg_start + 1]);
- return pc.Run() ? 0 : 1;
-}
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 5569da038..0df5664fd 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -18,7 +18,6 @@
#include <android-base/logging.h>
#include <android/snapshot/snapshot.pb.h>
-#include <storage_literals/storage_literals.h>
#include "dm_snapshot_internals.h"
#include "utility.h"
@@ -35,10 +34,6 @@ using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
namespace android {
namespace snapshot {
-static constexpr uint64_t kBlockSize = 4096;
-
-using namespace android::storage_literals;
-
// Intersect two linear extents. If no intersection, return an extent with length 0.
static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
// Convert target_extent and existing_extent to linear extents. Zero extents
@@ -142,23 +137,7 @@ void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Exte
}
}
-std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
- if (compression_enabled) {
- if (update == nullptr || !update->has_estimate_cow_size()) {
- LOG(ERROR) << "Update manifest does not include a COW size";
- return std::nullopt;
- }
-
- // Add an extra 2MB of wiggle room for any minor differences in labels/metadata
- // that might come up.
- auto size = update->estimate_cow_size() + 2_MiB;
-
- // Align to nearest block.
- size += kBlockSize - 1;
- size &= ~(kBlockSize - 1);
- return size;
- }
-
+uint64_t PartitionCowCreator::GetCowSize() {
// WARNING: The origin partition should be READ-ONLY
const uint64_t logical_block_size = current_metadata->logical_block_size();
const unsigned int sectors_per_block = logical_block_size / kSectorSize;
@@ -170,9 +149,9 @@ std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
WriteExtent(&sc, de, sectors_per_block);
}
- if (update == nullptr) return sc.cow_size_bytes();
+ if (operations == nullptr) return sc.cow_size_bytes();
- for (const auto& iop : update->operations()) {
+ for (const auto& iop : *operations) {
const InstallOperation* written_op = &iop;
InstallOperation buf;
// Do not allocate space for extents that are going to be skipped
@@ -202,10 +181,6 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
ret.snapshot_status.set_device_size(target_partition->size());
ret.snapshot_status.set_snapshot_size(target_partition->size());
- if (update && update->has_estimate_cow_size()) {
- ret.snapshot_status.set_estimated_cow_size(update->estimate_cow_size());
- }
-
if (ret.snapshot_status.snapshot_size() == 0) {
LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
ret.snapshot_status.set_cow_partition_size(0);
@@ -238,12 +213,9 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
auto cow_size = GetCowSize();
- if (!cow_size) {
- return {};
- }
// Compute the COW partition size.
- uint64_t cow_partition_size = std::min(cow_size.value(), free_region_length);
+ uint64_t cow_partition_size = std::min(cow_size, free_region_length);
// Round it down to the nearest logical block. Logical partitions must be a multiple
// of logical blocks.
cow_partition_size &= ~(logical_block_size - 1);
@@ -251,7 +223,7 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
// Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
ret.cow_partition_usable_regions = std::move(free_regions);
- auto cow_file_size = cow_size.value() - cow_partition_size;
+ auto cow_file_size = cow_size - cow_partition_size;
// Round it up to the nearest sector.
cow_file_size += kSectorSize - 1;
cow_file_size &= ~(kSectorSize - 1);
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 34b39ca72..699f9a1a4 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -36,7 +36,6 @@ struct PartitionCowCreator {
using MetadataBuilder = android::fs_mgr::MetadataBuilder;
using Partition = android::fs_mgr::Partition;
using InstallOperation = chromeos_update_engine::InstallOperation;
- using PartitionUpdate = chromeos_update_engine::PartitionUpdate;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
@@ -51,14 +50,11 @@ struct PartitionCowCreator {
MetadataBuilder* current_metadata = nullptr;
// The suffix of the current slot.
std::string current_suffix;
- // Partition information from the OTA manifest.
- const PartitionUpdate* update = nullptr;
+ // List of operations to be applied on the partition.
+ const RepeatedPtrField<InstallOperation>* operations = nullptr;
// Extra extents that are going to be invalidated during the update
// process.
std::vector<ChromeOSExtent> extra_extents = {};
- // True if compression is enabled.
- bool compression_enabled = false;
- std::string compression_algorithm;
struct Return {
SnapshotStatus snapshot_status;
@@ -69,7 +65,7 @@ struct PartitionCowCreator {
private:
bool HasExtent(Partition* p, Extent* e);
- std::optional<uint64_t> GetCowSize();
+ uint64_t GetCowSize();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index de35c132d..aca55b479 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -52,20 +52,20 @@ class PartitionCowCreatorTest : public ::testing::Test {
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
- constexpr uint64_t super_size = 1_MiB;
- constexpr uint64_t partition_size = 40_KiB;
+ constexpr uint64_t initial_size = 1_MiB;
+ constexpr uint64_t final_size = 40_KiB;
- auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
- auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
PartitionCowCreator creator{.target_metadata = builder_b.get(),
.target_suffix = "_b",
@@ -74,8 +74,8 @@ TEST_F(PartitionCowCreatorTest, IntersectSelf) {
.current_suffix = "_a"};
auto ret = creator.Run();
ASSERT_TRUE(ret.has_value());
- ASSERT_EQ(partition_size, ret->snapshot_status.device_size());
- ASSERT_EQ(partition_size, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(final_size, ret->snapshot_status.device_size());
+ ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
}
TEST_F(PartitionCowCreatorTest, Holes) {
@@ -124,20 +124,20 @@ TEST_F(PartitionCowCreatorTest, CowSize) {
using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
using Extent = chromeos_update_engine::Extent;
- constexpr uint64_t super_size = 50_MiB;
- constexpr uint64_t partition_size = 40_MiB;
+ constexpr uint64_t initial_size = 50_MiB;
+ constexpr uint64_t final_size = 40_MiB;
- auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
- auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
const uint64_t block_size = builder_b->logical_block_size();
const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
@@ -145,15 +145,13 @@ TEST_F(PartitionCowCreatorTest, CowSize) {
auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,
MetadataBuilder* builder_b, Partition* system_b) {
- PartitionUpdate update;
- *update.mutable_operations() = RepeatedInstallOperationPtr(iopv.begin(), iopv.end());
-
+ RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end());
PartitionCowCreator creator{.target_metadata = builder_b,
.target_suffix = "_b",
.target_partition = system_b,
.current_metadata = builder_a,
.current_suffix = "_a",
- .update = &update};
+ .operations = &riop};
auto ret = creator.Run();
@@ -205,83 +203,6 @@ TEST_F(PartitionCowCreatorTest, CowSize) {
ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
}
-TEST_F(PartitionCowCreatorTest, Zero) {
- constexpr uint64_t super_size = 1_MiB;
- auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
- ASSERT_NE(builder_a, nullptr);
-
- auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
- ASSERT_NE(builder_b, nullptr);
- auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
- ASSERT_NE(system_b, nullptr);
-
- PartitionCowCreator creator{.target_metadata = builder_b.get(),
- .target_suffix = "_b",
- .target_partition = system_b,
- .current_metadata = builder_a.get(),
- .current_suffix = "_a",
- .update = nullptr};
-
- auto ret = creator.Run();
-
- ASSERT_EQ(0u, ret->snapshot_status.device_size());
- ASSERT_EQ(0u, ret->snapshot_status.snapshot_size());
- ASSERT_EQ(0u, ret->snapshot_status.cow_file_size());
- ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
-}
-
-TEST_F(PartitionCowCreatorTest, CompressionEnabled) {
- constexpr uint64_t super_size = 1_MiB;
- auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
- ASSERT_NE(builder_a, nullptr);
-
- auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
- ASSERT_NE(builder_b, nullptr);
- auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
- ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));
-
- PartitionUpdate update;
- update.set_estimate_cow_size(256_KiB);
-
- PartitionCowCreator creator{.target_metadata = builder_b.get(),
- .target_suffix = "_b",
- .target_partition = system_b,
- .current_metadata = builder_a.get(),
- .current_suffix = "_a",
- .compression_enabled = true,
- .update = &update};
-
- auto ret = creator.Run();
- ASSERT_TRUE(ret.has_value());
- ASSERT_EQ(ret->snapshot_status.cow_file_size(), 1458176);
-}
-
-TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) {
- constexpr uint64_t super_size = 1_MiB;
- auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
- ASSERT_NE(builder_a, nullptr);
-
- auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
- ASSERT_NE(builder_b, nullptr);
- auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
- ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));
-
- PartitionUpdate update;
-
- PartitionCowCreator creator{.target_metadata = builder_b.get(),
- .target_suffix = "_b",
- .target_partition = system_b,
- .current_metadata = builder_a.get(),
- .current_suffix = "_a",
- .compression_enabled = true,
- .update = nullptr};
-
- auto ret = creator.Run();
- ASSERT_FALSE(ret.has_value());
-}
-
TEST(DmSnapshotInternals, CowSizeCalculator) {
SKIP_IF_NON_VIRTUAL_AB();
@@ -308,10 +229,6 @@ TEST(DmSnapshotInternals, CowSizeCalculator) {
cc.WriteByte(b);
ASSERT_EQ(cc.cow_size_sectors(), 40);
}
-
- // Write a byte that would surely overflow the counter
- cc.WriteChunk(std::numeric_limits<uint64_t>::max());
- ASSERT_FALSE(cc.cow_size_sectors().has_value());
}
void BlocksToExtents(const std::vector<uint64_t>& blocks,
diff --git a/fs_mgr/libsnapshot/power_test.cpp b/fs_mgr/libsnapshot/power_test.cpp
deleted file mode 100644
index 4d2548ae9..000000000
--- a/fs_mgr/libsnapshot/power_test.cpp
+++ /dev/null
@@ -1,559 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <iostream>
-#include <random>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/parsedouble.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fstab/fstab.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::dm::DmTable;
-using android::dm::DmTargetSnapshot;
-using android::dm::SnapshotStorageMode;
-using android::fiemap::ImageManager;
-using android::fs_mgr::Fstab;
-
-namespace android {
-namespace snapshot {
-
-static void usage() {
- std::cerr << "Usage:\n";
- std::cerr << " create <orig-payload> <new-payload>\n";
- std::cerr << "\n";
- std::cerr << " Create a snapshot device containing the contents of\n";
- std::cerr << " orig-payload, and then write the contents of new-payload.\n";
- std::cerr << " The original files are not modified.\n";
- std::cerr << "\n";
- std::cerr << " merge <fail-rate>\n";
- std::cerr << "\n";
- std::cerr << " Merge the snapshot previously started by create, and wait\n";
- std::cerr << " for it to complete. Once done, it is compared to the\n";
- std::cerr << " new-payload for consistency. The original files are not \n";
- std::cerr << " modified. If a fail-rate is passed (as a fraction between 0\n";
- std::cerr << " and 100), every 10ms the device has that percent change of\n";
- std::cerr << " injecting a kernel crash.\n";
- std::cerr << "\n";
- std::cerr << " check <new-payload>\n";
- std::cerr << " Verify that all artifacts are correct after a merge\n";
- std::cerr << " completes.\n";
- std::cerr << "\n";
- std::cerr << " cleanup\n";
- std::cerr << " Remove all ImageManager artifacts from create/merge.\n";
-}
-
-class PowerTest final {
- public:
- PowerTest();
- bool Run(int argc, char** argv);
-
- private:
- bool OpenImageManager();
- bool Create(int argc, char** argv);
- bool Merge(int argc, char** argv);
- bool Check(int argc, char** argv);
- bool Cleanup();
- bool CleanupImage(const std::string& name);
- bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
- bool MapImages();
- bool MapSnapshot(SnapshotStorageMode mode);
- bool GetMergeStatus(DmTargetSnapshot::Status* status);
-
- static constexpr char kSnapshotName[] = "snapshot-power-test";
- static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
- static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
-
- DeviceMapper& dm_;
- std::unique_ptr<ImageManager> images_;
- std::string image_path_;
- std::string cow_path_;
- std::string snapshot_path_;
-};
-
-PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
-
-bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
- if (!OpenImageManager()) {
- return false;
- }
-
- if (argc < 2) {
- usage();
- return false;
- }
- if (argv[1] == "create"s) {
- return Create(argc, argv);
- } else if (argv[1] == "merge"s) {
- return Merge(argc, argv);
- } else if (argv[1] == "check"s) {
- return Check(argc, argv);
- } else if (argv[1] == "cleanup"s) {
- return Cleanup();
- } else {
- usage();
- return false;
- }
-}
-
-bool PowerTest::OpenImageManager() {
- std::vector<std::string> dirs = {
- "/data/gsi/test",
- "/metadata/gsi/test",
- };
- for (const auto& dir : dirs) {
- if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
- std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
- return false;
- }
- }
-
- images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
- if (!images_) {
- std::cerr << "Could not open ImageManager\n";
- return false;
- }
- return true;
-}
-
-bool PowerTest::Create(int argc, char** argv) {
- if (argc < 4) {
- usage();
- return false;
- }
-
- std::string first = argv[2];
- std::string second = argv[3];
-
- unique_fd second_fd(open(second.c_str(), O_RDONLY));
- if (second_fd < 0) {
- std::cerr << "open " << second << ": " << strerror(errno) << "\n";
- return false;
- }
-
- if (!Cleanup()) {
- return false;
- }
- if (!SetupImages(first, second_fd)) {
- return false;
- }
- if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
- return false;
- }
-
- struct stat s;
- if (fstat(second_fd, &s)) {
- std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
- return false;
- }
-
- unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
- if (snap_fd < 0) {
- std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
- return false;
- }
-
- uint8_t chunk[4096];
- uint64_t written = 0;
- while (written < s.st_size) {
- uint64_t remaining = s.st_size - written;
- size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
- if (!android::base::ReadFully(second_fd, chunk, bytes)) {
- std::cerr << "read " << second << ": " << strerror(errno) << "\n";
- return false;
- }
- if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
- std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
- return false;
- }
- written += bytes;
- }
- if (fsync(snap_fd)) {
- std::cerr << "fsync: " << strerror(errno) << "\n";
- return false;
- }
-
- sync();
-
- snap_fd = {};
- if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
- std::cerr << "could not delete dm device " << kSnapshotName << "\n";
- return false;
- }
- if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
- std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
- return false;
- }
- if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
- std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
- return false;
- }
- return true;
-}
-
-bool PowerTest::Cleanup() {
- if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
- std::cerr << "could not delete dm device " << kSnapshotName << "\n";
- return false;
- }
- if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
- return false;
- }
- return true;
-}
-
-bool PowerTest::CleanupImage(const std::string& name) {
- if (!images_->UnmapImageIfExists(name)) {
- std::cerr << "failed to unmap " << name << "\n";
- return false;
- }
- if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
- std::cerr << "failed to delete " << name << "\n";
- return false;
- }
- return true;
-}
-
-bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
- unique_fd first_fd(open(first.c_str(), O_RDONLY));
- if (first_fd < 0) {
- std::cerr << "open " << first << ": " << strerror(errno) << "\n";
- return false;
- }
-
- struct stat s1, s2;
- if (fstat(first_fd.get(), &s1)) {
- std::cerr << "first stat: " << strerror(errno) << "\n";
- return false;
- }
- if (fstat(second_fd.get(), &s2)) {
- std::cerr << "second stat: " << strerror(errno) << "\n";
- return false;
- }
-
- // Pick the bigger size of both images, rounding up to the nearest block.
- uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
- uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
- uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
- if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
- std::cerr << "failed to create " << kSnapshotImageName << "\n";
- return false;
- }
- // Use the same size for the cow.
- if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
- std::cerr << "failed to create " << kSnapshotCowName << "\n";
- return false;
- }
- if (!MapImages()) {
- return false;
- }
-
- unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
- if (image_fd < 0) {
- std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
- return false;
- }
-
- uint8_t chunk[4096];
- uint64_t written = 0;
- while (written < s1.st_size) {
- uint64_t remaining = s1.st_size - written;
- size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
- if (!android::base::ReadFully(first_fd, chunk, bytes)) {
- std::cerr << "read: " << strerror(errno) << "\n";
- return false;
- }
- if (!android::base::WriteFully(image_fd, chunk, bytes)) {
- std::cerr << "write: " << strerror(errno) << "\n";
- return false;
- }
- written += bytes;
- }
- if (fsync(image_fd)) {
- std::cerr << "fsync: " << strerror(errno) << "\n";
- return false;
- }
-
- // Zero the first block of the COW.
- unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
- if (cow_fd < 0) {
- std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
- return false;
- }
-
- memset(chunk, 0, sizeof(chunk));
- if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
- std::cerr << "read: " << strerror(errno) << "\n";
- return false;
- }
- if (fsync(cow_fd)) {
- std::cerr << "fsync: " << strerror(errno) << "\n";
- return false;
- }
- return true;
-}
-
-bool PowerTest::MapImages() {
- if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
- std::cerr << "failed to map " << kSnapshotImageName << "\n";
- return false;
- }
- if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
- std::cerr << "failed to map " << kSnapshotCowName << "\n";
- return false;
- }
- return true;
-}
-
-bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
- uint64_t sectors;
- {
- unique_fd fd(open(image_path_.c_str(), O_RDONLY));
- if (fd < 0) {
- std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
- return false;
- }
- sectors = get_block_device_size(fd) / 512;
- }
-
- DmTable table;
- table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
- if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
- std::cerr << "failed to create snapshot device\n";
- return false;
- }
- return true;
-}
-
-bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
- std::vector<DeviceMapper::TargetInfo> targets;
- if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
- std::cerr << "failed to get merge status\n";
- return false;
- }
- if (targets.size() != 1) {
- std::cerr << "merge device has wrong number of targets\n";
- return false;
- }
- if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
- std::cerr << "could not parse merge target status text\n";
- return false;
- }
- return true;
-}
-
-static std::string GetUserdataBlockDeviceName() {
- Fstab fstab;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
- return {};
- }
-
- auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
- if (!entry) {
- return {};
- }
-
- auto prefix = "/dev/block/"s;
- if (!android::base::StartsWith(entry->blk_device, prefix)) {
- return {};
- }
- return entry->blk_device.substr(prefix.size());
-}
-
-bool PowerTest::Merge(int argc, char** argv) {
- // Start an f2fs GC to really stress things. :TODO: figure out data device
- auto userdata_dev = GetUserdataBlockDeviceName();
- if (userdata_dev.empty()) {
- std::cerr << "could not locate userdata block device\n";
- return false;
- }
-
- auto cmd =
- android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
- system(cmd.c_str());
-
- if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
- if (!MapImages()) {
- return false;
- }
- if (!MapSnapshot(SnapshotStorageMode::Merge)) {
- return false;
- }
- }
-
- std::random_device r;
- std::default_random_engine re(r());
- std::uniform_real_distribution<double> dist(0.0, 100.0);
-
- std::optional<double> failure_rate;
- if (argc >= 3) {
- double d;
- if (!android::base::ParseDouble(argv[2], &d)) {
- std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
- return false;
- }
- failure_rate = d;
- }
-
- while (true) {
- DmTargetSnapshot::Status status;
- if (!GetMergeStatus(&status)) {
- return false;
- }
- if (!status.error.empty()) {
- std::cerr << "merge reported error: " << status.error << "\n";
- return false;
- }
- if (status.sectors_allocated == status.metadata_sectors) {
- break;
- }
-
- std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
-
- if (failure_rate && *failure_rate >= dist(re)) {
- system("echo 1 > /proc/sys/kernel/sysrq");
- system("echo c > /proc/sysrq-trigger");
- }
-
- std::this_thread::sleep_for(10ms);
- }
-
- std::cout << "Merge completed.\n";
- return true;
-}
-
-bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
- if (argc < 3) {
- std::cerr << "Expected argument: <new-image-path>\n";
- return false;
- }
- std::string md_path, image_path;
- std::string canonical_path = argv[2];
-
- if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
- std::cerr << "could not get dm-path for merge device\n";
- return false;
- }
- if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
- std::cerr << "could not get image path\n";
- return false;
- }
-
- unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
- if (md_fd < 0) {
- std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
- return false;
- }
- unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
- if (image_fd < 0) {
- std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
- return false;
- }
- unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
- if (canonical_fd < 0) {
- std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
- return false;
- }
-
- struct stat s;
- if (fstat(canonical_fd, &s)) {
- std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
- return false;
- }
- uint64_t canonical_size = s.st_size;
- uint64_t md_size = get_block_device_size(md_fd);
- uint64_t image_size = get_block_device_size(image_fd);
- if (image_size != md_size) {
- std::cerr << "image size does not match merge device size\n";
- return false;
- }
- if (canonical_size > image_size) {
- std::cerr << "canonical size " << canonical_size << " is greater than image size "
- << image_size << "\n";
- return false;
- }
-
- constexpr size_t kBlockSize = 4096;
- uint8_t canonical_buffer[kBlockSize];
- uint8_t image_buffer[kBlockSize];
- uint8_t md_buffer[kBlockSize];
-
- uint64_t remaining = canonical_size;
- uint64_t blockno = 0;
- while (remaining) {
- size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
- if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
- std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
- return false;
- }
- if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
- std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
- return false;
- }
- if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
- std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
- return false;
- }
- if (memcmp(canonical_buffer, image_buffer, bytes)) {
- std::cerr << "canonical and image differ at block " << blockno << "\n";
- return false;
- }
- if (memcmp(canonical_buffer, md_buffer, bytes)) {
- std::cerr << "canonical and image differ at block " << blockno << "\n";
- return false;
- }
-
- remaining -= bytes;
- blockno++;
- }
-
- std::cout << "Images all match.\n";
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
-
-int main(int argc, char** argv) {
- android::snapshot::PowerTest test;
-
- if (!test.Run(argc, argv)) {
- std::cerr << "Unexpected error running test." << std::endl;
- return 1;
- }
- fflush(stdout);
- return 0;
-}
diff --git a/fs_mgr/libsnapshot/run_power_test.sh b/fs_mgr/libsnapshot/run_power_test.sh
deleted file mode 100755
index dc03dc975..000000000
--- a/fs_mgr/libsnapshot/run_power_test.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-set -e
-
-if [ -z "$FAIL_RATE" ]; then
- FAIL_RATE=5.0
-fi
-if [ ! -z "$ANDROID_SERIAL" ]; then
- DEVICE_ARGS=-s $ANDROID_SERIAL
-else
- DEVICE_ARGS=
-fi
-
-TEST_BIN=/data/nativetest64/snapshot_power_test/snapshot_power_test
-
-while :
-do
- adb $DEVICE_ARGS wait-for-device
- adb $DEVICE_ARGS root
- adb $DEVICE_ARGS shell rm $TEST_BIN
- adb $DEVICE_ARGS sync data
- set +e
- output=$(adb $DEVICE_ARGS shell $TEST_BIN merge $FAIL_RATE 2>&1)
- set -e
- if [[ "$output" == *"Merge completed"* ]]; then
- echo "Merge completed."
- break
- fi
- if [[ "$output" == *"Unexpected error"* ]]; then
- echo "Unexpected error."
- exit 1
- fi
-done
-
-adb $DEVICE_ARGS shell $TEST_BIN check $1
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0e36da151..4178349ed 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -15,13 +15,11 @@
#include <libsnapshot/snapshot.h>
#include <dirent.h>
-#include <fcntl.h>
#include <math.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/unistd.h>
-#include <filesystem>
#include <optional>
#include <thread>
#include <unordered_set>
@@ -29,13 +27,10 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <cutils/sockets.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
-#include <fs_mgr/file_wait.h>
#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
@@ -47,7 +42,6 @@
#include "device_info.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
-#include "snapshot_reader.h"
#include "utility.h"
namespace android {
@@ -59,7 +53,6 @@ using android::dm::DmDeviceState;
using android::dm::DmTable;
using android::dm::DmTargetLinear;
using android::dm::DmTargetSnapshot;
-using android::dm::DmTargetUser;
using android::dm::kSectorSize;
using android::dm::SnapshotStorageMode;
using android::fiemap::FiemapStatus;
@@ -75,8 +68,7 @@ using android::fs_mgr::SlotNumberForSlotSuffix;
using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
-using chromeos_update_engine::FileDescriptor;
-using chromeos_update_engine::PartitionUpdate;
+using chromeos_update_engine::InstallOperation;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
using std::chrono::duration_cast;
@@ -99,22 +91,15 @@ std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
}
std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
- if (!info) {
- DeviceInfo* impl = new DeviceInfo();
- impl->set_first_stage_init(true);
- info = impl;
- }
auto sm = New(info);
-
- // The first-stage version of snapuserd is explicitly started by init. Do
- // not attempt to using it during tests (which run in normal AOSP).
- if (!sm->device()->IsTestDevice()) {
- sm->use_first_stage_snapuserd_ = true;
+ if (!sm || !sm->ForceLocalImageManager()) {
+ return nullptr;
}
return sm;
}
SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
+ gsid_dir_ = device_->GetGsidDir();
metadata_dir_ = device_->GetMetadataDir();
}
@@ -122,10 +107,6 @@ static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
}
-static std::string GetDmUserCowName(const std::string& snapshot_name) {
- return snapshot_name + "-user-cow";
-}
-
static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
return snapshot_name + "-cow-img";
}
@@ -134,8 +115,8 @@ static std::string GetBaseDeviceName(const std::string& partition_name) {
return partition_name + "-base";
}
-static std::string GetSourceDeviceName(const std::string& partition_name) {
- return partition_name + "-src";
+static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
+ return snapshot_name + "-inner";
}
bool SnapshotManager::BeginUpdate() {
@@ -159,9 +140,6 @@ bool SnapshotManager::BeginUpdate() {
images_->RemoveAllImages();
}
- // Clear any cached metadata (this allows re-using one manager across tests).
- old_partition_metadata_ = nullptr;
-
auto state = ReadUpdateState(file.get());
if (state != UpdateState::None) {
LOG(ERROR) << "An update is already in progress, cannot begin a new update";
@@ -228,15 +206,6 @@ SnapshotManager::Slot SnapshotManager::GetCurrentSlot() {
return Slot::Target;
}
-std::string SnapshotManager::GetSnapshotSlotSuffix() {
- switch (GetCurrentSlot()) {
- case Slot::Target:
- return device_->GetSlotSuffix();
- default:
- return device_->GetOtherSlotSuffix();
- }
-}
-
static bool RemoveFileIfExists(const std::string& path) {
std::string message;
if (!android::base::RemoveFileIfExists(path, &message)) {
@@ -269,7 +238,6 @@ bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function
GetSnapshotBootIndicatorPath(),
GetRollbackIndicatorPath(),
GetForwardMergeIndicatorPath(),
- GetOldPartitionMetadataPath(),
};
for (const auto& file : files) {
RemoveFileIfExists(file);
@@ -323,8 +291,7 @@ bool SnapshotManager::FinishedSnapshotWrites(bool wipe) {
return WriteUpdateState(lock.get(), UpdateState::Unverified);
}
-bool SnapshotManager::CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_creator,
- SnapshotStatus* status) {
+bool SnapshotManager::CreateSnapshot(LockedFile* lock, SnapshotStatus* status) {
CHECK(lock);
CHECK(lock->lock_mode() == LOCK_EX);
CHECK(status);
@@ -333,9 +300,9 @@ bool SnapshotManager::CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_
LOG(ERROR) << "SnapshotStatus has no name.";
return false;
}
- // Check these sizes. Like liblp, we guarantee the partition size is
- // respected, which means it has to be sector-aligned. (This guarantee is
- // useful for locating avb footers correctly). The COW file size, however,
+ // Sanity check these sizes. Like liblp, we guarantee the partition size
+ // is respected, which means it has to be sector-aligned. (This guarantee
+ // is useful for locating avb footers correctly). The COW file size, however,
// can be arbitrarily larger than specified, so we can safely round it up.
if (status->device_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << status->name()
@@ -365,8 +332,6 @@ bool SnapshotManager::CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_
status->set_state(SnapshotState::CREATED);
status->set_sectors_allocated(0);
status->set_metadata_sectors(0);
- status->set_compression_enabled(cow_creator->compression_enabled);
- status->set_compression_algorithm(cow_creator->compression_algorithm);
if (!WriteSnapshotStatus(lock, *status)) {
PLOG(ERROR) << "Could not write snapshot status: " << status->name();
@@ -386,6 +351,7 @@ Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name
}
// The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
+ // Sanity check this.
if (status.cow_file_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
<< status.cow_file_size();
@@ -397,47 +363,6 @@ Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name
return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
}
-bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
- const std::string& cow_file, const std::string& base_device,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- CHECK(lock);
-
- auto& dm = DeviceMapper::Instance();
-
- // Use an extra decoration for first-stage init, so we can transition
- // to a new table entry in second-stage.
- std::string misc_name = name;
- if (use_first_stage_snapuserd_) {
- misc_name += "-init";
- }
-
- if (!EnsureSnapuserdConnected()) {
- return false;
- }
-
- uint64_t base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device);
- if (base_sectors == 0) {
- LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
- return false;
- }
-
- DmTable table;
- table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
- if (!dm.CreateDevice(name, table, path, timeout_ms)) {
- return false;
- }
- if (!WaitForDevice(*path, timeout_ms)) {
- return false;
- }
-
- auto control_device = "/dev/dm-user/" + misc_name;
- if (!WaitForDevice(control_device, timeout_ms)) {
- return false;
- }
-
- return snapuserd_client_->AttachDmUser(misc_name);
-}
-
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
const std::string& base_device, const std::string& cow_device,
const std::chrono::milliseconds& timeout_ms,
@@ -482,13 +407,8 @@ bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
LOG(ERROR) << "Invalid snapshot size for " << base_device << ": " << status.snapshot_size();
return false;
}
- if (status.device_size() != status.snapshot_size()) {
- LOG(ERROR) << "Device size and snapshot size must be the same (device size = "
- << status.device_size() << ", snapshot size = " << status.snapshot_size();
- return false;
- }
-
uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
+ uint64_t linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize;
auto& dm = DeviceMapper::Instance();
@@ -496,8 +416,7 @@ bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
// have completed merging, but the start of the merge process is considered
// atomic.
SnapshotStorageMode mode;
- SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- switch (update_status.state()) {
+ switch (ReadUpdateState(lock)) {
case UpdateState::MergeCompleted:
case UpdateState::MergeNeedsReboot:
LOG(ERROR) << "Should not create a snapshot device for " << name
@@ -507,24 +426,52 @@ bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
case UpdateState::MergeFailed:
// Note: MergeFailed indicates that a merge is in progress, but
// is possibly stalled. We still have to honor the merge.
- if (DecideMergePhase(status) == update_status.merge_phase()) {
- mode = SnapshotStorageMode::Merge;
- } else {
- mode = SnapshotStorageMode::Persistent;
- }
+ mode = SnapshotStorageMode::Merge;
break;
default:
mode = SnapshotStorageMode::Persistent;
break;
}
+ // The kernel (tested on 4.19) crashes horribly if a device has both a snapshot
+ // and a linear target in the same table. Instead, we stack them, and give the
+ // snapshot device a different name. It is not exposed to the caller in this
+ // case.
+ auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
+
DmTable table;
table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
- if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
- LOG(ERROR) << "Could not create snapshot device: " << name;
+ if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
+ LOG(ERROR) << "Could not create snapshot device: " << snap_name;
return false;
}
+
+ if (linear_sectors) {
+ std::string snap_dev;
+ if (!dm.GetDeviceString(snap_name, &snap_dev)) {
+ LOG(ERROR) << "Cannot determine major/minor for: " << snap_name;
+ return false;
+ }
+
+ // Our stacking will looks like this:
+ // [linear, linear] ; to snapshot, and non-snapshot region of base device
+ // [snapshot-inner]
+ // [base device] [cow]
+ DmTable table;
+ table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0);
+ table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device,
+ snapshot_sectors);
+ if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+ LOG(ERROR) << "Could not create outer snapshot device: " << name;
+ dm.DeleteDevice(snap_name);
+ return false;
+ }
+ }
+
+ // :TODO: when merging is implemented, we need to add an argument to the
+ // status indicating how much progress is left to merge. (device-mapper
+ // does not retain the initial values, so we can't derive them.)
return true;
}
@@ -535,7 +482,9 @@ std::optional<std::string> SnapshotManager::MapCowImage(
bool ok;
std::string cow_dev;
- if (device_->IsRecovery() || device_->IsFirstStageInit()) {
+ if (has_local_image_manager_) {
+ // If we forced a local image manager, it means we don't have binder,
+ // which means first-stage init. We must use device-mapper.
const auto& opener = device_->GetPartitionOpener();
ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev);
} else {
@@ -550,43 +499,21 @@ std::optional<std::string> SnapshotManager::MapCowImage(
return std::nullopt;
}
-bool SnapshotManager::MapSourceDevice(LockedFile* lock, const std::string& name,
- const std::chrono::milliseconds& timeout_ms,
- std::string* path) {
+bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
CHECK(lock);
- auto metadata = ReadOldPartitionMetadata(lock);
- if (!metadata) {
- LOG(ERROR) << "Could not map source device due to missing or corrupt metadata";
+ auto& dm = DeviceMapper::Instance();
+ if (!dm.DeleteDeviceIfExists(name)) {
+ LOG(ERROR) << "Could not delete snapshot device: " << name;
return false;
}
- auto old_name = GetOtherPartitionName(name);
- auto slot_suffix = device_->GetSlotSuffix();
- auto slot = SlotNumberForSlotSuffix(slot_suffix);
-
- CreateLogicalPartitionParams params = {
- .block_device = device_->GetSuperDevice(slot),
- .metadata = metadata,
- .partition_name = old_name,
- .timeout_ms = timeout_ms,
- .device_name = GetSourceDeviceName(name),
- .partition_opener = &device_->GetPartitionOpener(),
- };
- if (!CreateLogicalPartition(std::move(params), path)) {
- LOG(ERROR) << "Could not create source device for snapshot " << name;
+ auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
+ if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
+ LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
return false;
}
- return true;
-}
-bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
- CHECK(lock);
-
- if (!DeleteDeviceIfExists(name)) {
- LOG(ERROR) << "Could not delete snapshot device: " << name;
- return false;
- }
return true;
}
@@ -628,7 +555,7 @@ bool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name)
return true;
}
-bool SnapshotManager::InitiateMerge() {
+bool SnapshotManager::InitiateMerge(uint64_t* cow_file_size) {
auto lock = LockExclusive();
if (!lock) return false;
@@ -691,10 +618,7 @@ bool SnapshotManager::InitiateMerge() {
}
}
- bool compression_enabled = false;
-
- std::vector<std::string> first_merge_group;
-
+ uint64_t total_cow_file_size = 0;
DmTargetSnapshot::Status initial_target_values = {};
for (const auto& snapshot : snapshots) {
DmTargetSnapshot::Status current_status;
@@ -709,67 +633,51 @@ bool SnapshotManager::InitiateMerge() {
if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
return false;
}
+ total_cow_file_size += snapshot_status.cow_file_size();
+ }
- compression_enabled |= snapshot_status.compression_enabled();
- if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
- first_merge_group.emplace_back(snapshot);
- }
+ if (cow_file_size) {
+ *cow_file_size = total_cow_file_size;
}
- SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
+ SnapshotUpdateStatus initial_status;
initial_status.set_state(UpdateState::Merging);
initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
initial_status.set_total_sectors(initial_target_values.total_sectors);
initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
- initial_status.set_compression_enabled(compression_enabled);
-
- // If any partitions shrunk, we need to merge them before we merge any other
- // partitions (see b/177935716). Otherwise, a merge from another partition
- // may overwrite the source block of a copy operation.
- const std::vector<std::string>* merge_group;
- if (first_merge_group.empty()) {
- merge_group = &snapshots;
- initial_status.set_merge_phase(MergePhase::SECOND_PHASE);
- } else {
- merge_group = &first_merge_group;
- initial_status.set_merge_phase(MergePhase::FIRST_PHASE);
- }
// Point of no return - mark that we're starting a merge. From now on every
- // eligible snapshot must be a merge target.
+ // snapshot must be a merge target.
if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {
return false;
}
- auto reported_code = MergeFailureCode::Ok;
- for (const auto& snapshot : *merge_group) {
+ bool rewrote_all = true;
+ for (const auto& snapshot : snapshots) {
// If this fails, we have no choice but to continue. Everything must
// be merged. This is not an ideal state to be in, but it is safe,
// because we the next boot will try again.
- auto code = SwitchSnapshotToMerge(lock.get(), snapshot);
- if (code != MergeFailureCode::Ok) {
+ if (!SwitchSnapshotToMerge(lock.get(), snapshot)) {
LOG(ERROR) << "Failed to switch snapshot to a merge target: " << snapshot;
- if (reported_code == MergeFailureCode::Ok) {
- reported_code = code;
- }
+ rewrote_all = false;
}
}
// If we couldn't switch everything to a merge target, pre-emptively mark
// this merge as failed. It will get acknowledged when WaitForMerge() is
// called.
- if (reported_code != MergeFailureCode::Ok) {
- WriteUpdateState(lock.get(), UpdateState::MergeFailed, reported_code);
+ if (!rewrote_all) {
+ WriteUpdateState(lock.get(), UpdateState::MergeFailed);
}
// Return true no matter what, because a merge was initiated.
return true;
}
-MergeFailureCode SnapshotManager::SwitchSnapshotToMerge(LockedFile* lock, const std::string& name) {
+bool SnapshotManager::SwitchSnapshotToMerge(LockedFile* lock, const std::string& name) {
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, name, &status)) {
- return MergeFailureCode::ReadStatus;
+ return false;
}
if (status.state() != SnapshotState::CREATED) {
LOG(WARNING) << "Snapshot " << name
@@ -778,52 +686,53 @@ MergeFailureCode SnapshotManager::SwitchSnapshotToMerge(LockedFile* lock, const
// After this, we return true because we technically did switch to a merge
// target. Everything else we do here is just informational.
- if (auto code = RewriteSnapshotDeviceTable(name); code != MergeFailureCode::Ok) {
- return code;
+ auto dm_name = GetSnapshotDeviceName(name, status);
+ if (!RewriteSnapshotDeviceTable(dm_name)) {
+ return false;
}
status.set_state(SnapshotState::MERGING);
DmTargetSnapshot::Status dm_status;
- if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {
- LOG(ERROR) << "Could not query merge status for snapshot: " << name;
+ if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
+ LOG(ERROR) << "Could not query merge status for snapshot: " << dm_name;
}
status.set_sectors_allocated(dm_status.sectors_allocated);
status.set_metadata_sectors(dm_status.metadata_sectors);
if (!WriteSnapshotStatus(lock, status)) {
LOG(ERROR) << "Could not update status file for snapshot: " << name;
}
- return MergeFailureCode::Ok;
+ return true;
}
-MergeFailureCode SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {
+bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& dm_name) {
auto& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> old_targets;
- if (!dm.GetTableInfo(name, &old_targets)) {
- LOG(ERROR) << "Could not read snapshot device table: " << name;
- return MergeFailureCode::GetTableInfo;
+ if (!dm.GetTableInfo(dm_name, &old_targets)) {
+ LOG(ERROR) << "Could not read snapshot device table: " << dm_name;
+ return false;
}
if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != "snapshot") {
- LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << name;
- return MergeFailureCode::UnknownTable;
+ LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << dm_name;
+ return false;
}
std::string base_device, cow_device;
if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {
- LOG(ERROR) << "Could not derive underlying devices for snapshot: " << name;
- return MergeFailureCode::GetTableParams;
+ LOG(ERROR) << "Could not derive underlying devices for snapshot: " << dm_name;
+ return false;
}
DmTable table;
table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
SnapshotStorageMode::Merge, kSnapshotChunkSize);
- if (!dm.LoadTableAndActivate(name, table)) {
- LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << name;
- return MergeFailureCode::ActivateNewTable;
+ if (!dm.LoadTableAndActivate(dm_name, table)) {
+ LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << dm_name;
+ return false;
}
- LOG(INFO) << "Successfully switched snapshot device to a merge target: " << name;
- return MergeFailureCode::Ok;
+ LOG(INFO) << "Successfully switched snapshot device to a merge target: " << dm_name;
+ return true;
}
enum class TableQuery {
@@ -895,20 +804,18 @@ bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::strin
UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& callback,
const std::function<bool()>& before_cancel) {
while (true) {
- auto result = CheckMergeState(before_cancel);
- LOG(INFO) << "ProcessUpdateState handling state: " << result.state;
-
- if (result.state == UpdateState::MergeFailed) {
- AcknowledgeMergeFailure(result.failure_code);
+ UpdateState state = CheckMergeState(before_cancel);
+ if (state == UpdateState::MergeFailed) {
+ AcknowledgeMergeFailure();
}
- if (result.state != UpdateState::Merging) {
+ if (state != UpdateState::Merging) {
// Either there is no merge, or the merge was finished, so no need
// to keep waiting.
- return result.state;
+ return state;
}
if (callback && !callback()) {
- return result.state;
+ return state;
}
// This wait is not super time sensitive, so we have a relatively
@@ -917,36 +824,34 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& cal
}
}
-auto SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) -> MergeResult {
+UpdateState SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) {
auto lock = LockExclusive();
if (!lock) {
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::AcquireLock);
+ return UpdateState::MergeFailed;
}
- auto result = CheckMergeState(lock.get(), before_cancel);
- LOG(INFO) << "CheckMergeState for snapshots returned: " << result.state;
-
- if (result.state == UpdateState::MergeCompleted) {
+ UpdateState state = CheckMergeState(lock.get(), before_cancel);
+ if (state == UpdateState::MergeCompleted) {
// Do this inside the same lock. Failures get acknowledged without the
// lock, because flock() might have failed.
AcknowledgeMergeSuccess(lock.get());
- } else if (result.state == UpdateState::Cancelled) {
- if (!device_->IsRecovery() && !RemoveAllUpdateState(lock.get(), before_cancel)) {
- LOG(ERROR) << "Failed to remove all update state after acknowleding cancelled update.";
+ } else if (state == UpdateState::Cancelled) {
+ if (!RemoveAllUpdateState(lock.get(), before_cancel)) {
+ return ReadSnapshotUpdateStatus(lock.get()).state();
}
}
- return result;
+ return state;
}
-auto SnapshotManager::CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel)
- -> MergeResult {
- SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- switch (update_status.state()) {
+UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
+ const std::function<bool()>& before_cancel) {
+ UpdateState state = ReadUpdateState(lock);
+ switch (state) {
case UpdateState::None:
case UpdateState::MergeCompleted:
// Harmless races are allowed between two callers of WaitForMerge,
// so in both of these cases we just propagate the state.
- return MergeResult(update_status.state());
+ return state;
case UpdateState::Merging:
case UpdateState::MergeNeedsReboot:
@@ -961,42 +866,28 @@ auto SnapshotManager::CheckMergeState(LockedFile* lock, const std::function<bool
// via the merge poll below, but if we never started a merge, we
// need to also check here.
if (HandleCancelledUpdate(lock, before_cancel)) {
- return MergeResult(UpdateState::Cancelled);
+ return UpdateState::Cancelled;
}
- return MergeResult(update_status.state());
+ return state;
default:
- return MergeResult(update_status.state());
+ return state;
}
std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) {
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ListSnapshots);
+ return UpdateState::MergeFailed;
}
- auto other_suffix = device_->GetOtherSlotSuffix();
-
bool cancelled = false;
+ bool failed = false;
bool merging = false;
bool needs_reboot = false;
- bool wrong_phase = false;
- MergeFailureCode failure_code = MergeFailureCode::Ok;
for (const auto& snapshot : snapshots) {
- if (android::base::EndsWith(snapshot, other_suffix)) {
- // This will have triggered an error message in InitiateMerge already.
- LOG(INFO) << "Skipping merge validation of unexpected snapshot: " << snapshot;
- continue;
- }
-
- auto result = CheckTargetMergeState(lock, snapshot, update_status);
- LOG(INFO) << "CheckTargetMergeState for " << snapshot << " returned: " << result.state;
-
- switch (result.state) {
+ UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot);
+ switch (snapshot_state) {
case UpdateState::MergeFailed:
- // Take the first failure code in case other failures compound.
- if (failure_code == MergeFailureCode::Ok) {
- failure_code = result.failure_code;
- }
+ failed = true;
break;
case UpdateState::Merging:
merging = true;
@@ -1009,15 +900,10 @@ auto SnapshotManager::CheckMergeState(LockedFile* lock, const std::function<bool
case UpdateState::Cancelled:
cancelled = true;
break;
- case UpdateState::None:
- wrong_phase = true;
- break;
default:
LOG(ERROR) << "Unknown merge status for \"" << snapshot << "\": "
- << "\"" << result.state << "\"";
- if (failure_code == MergeFailureCode::Ok) {
- failure_code = MergeFailureCode::UnexpectedMergeState;
- }
+ << "\"" << snapshot_state << "\"";
+ failed = true;
break;
}
}
@@ -1026,25 +912,16 @@ auto SnapshotManager::CheckMergeState(LockedFile* lock, const std::function<bool
// Note that we handle "Merging" before we handle anything else. We
// want to poll until *nothing* is merging if we can, so everything has
// a chance to get marked as completed or failed.
- return MergeResult(UpdateState::Merging);
+ return UpdateState::Merging;
}
- if (failure_code != MergeFailureCode::Ok) {
+ if (failed) {
// Note: since there are many drop-out cases for failure, we acknowledge
// it in WaitForMerge rather than here and elsewhere.
- return MergeResult(UpdateState::MergeFailed, failure_code);
- }
- if (wrong_phase) {
- // If we got here, no other partitions are being merged, and nothing
- // failed to merge. It's safe to move to the next merge phase.
- auto code = MergeSecondPhaseSnapshots(lock);
- if (code != MergeFailureCode::Ok) {
- return MergeResult(UpdateState::MergeFailed, code);
- }
- return MergeResult(UpdateState::Merging);
+ return UpdateState::MergeFailed;
}
if (needs_reboot) {
WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
- return MergeResult(UpdateState::MergeNeedsReboot);
+ return UpdateState::MergeNeedsReboot;
}
if (cancelled) {
// This is an edge case, that we handle as correctly as we sensibly can.
@@ -1052,22 +929,22 @@ auto SnapshotManager::CheckMergeState(LockedFile* lock, const std::function<bool
// removed the snapshot as a result. The exact state of the update is
// undefined now, but this can only happen on an unlocked device where
// partitions can be flashed without wiping userdata.
- return MergeResult(UpdateState::Cancelled);
+ return UpdateState::Cancelled;
}
- return MergeResult(UpdateState::MergeCompleted);
+ return UpdateState::MergeCompleted;
}
-auto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name,
- const SnapshotUpdateStatus& update_status)
- -> MergeResult {
+UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name) {
SnapshotStatus snapshot_status;
if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ReadStatus);
+ return UpdateState::MergeFailed;
}
+ std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
+
std::unique_ptr<LpMetadata> current_metadata;
- if (!IsSnapshotDevice(name)) {
+ if (!IsSnapshotDevice(dm_name)) {
if (!current_metadata) {
current_metadata = ReadCurrentMetadata();
}
@@ -1075,22 +952,22 @@ auto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string&
if (!current_metadata ||
GetMetadataPartitionState(*current_metadata, name) != MetadataPartitionState::Updated) {
DeleteSnapshot(lock, name);
- return MergeResult(UpdateState::Cancelled);
+ return UpdateState::Cancelled;
}
// During a check, we decided the merge was complete, but we were unable to
// collapse the device-mapper stack and perform COW cleanup. If we haven't
// rebooted after this check, the device will still be a snapshot-merge
- // target. If we have rebooted, the device will now be a linear target,
+ // target. If the have rebooted, the device will now be a linear target,
// and we can try cleanup again.
if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
// NB: It's okay if this fails now, we gave cleanup our best effort.
OnSnapshotMergeComplete(lock, name, snapshot_status);
- return MergeResult(UpdateState::MergeCompleted);
+ return UpdateState::MergeCompleted;
}
- LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << name;
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);
+ LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
+ return UpdateState::MergeFailed;
}
// This check is expensive so it is only enabled for debugging.
@@ -1099,36 +976,24 @@ auto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string&
std::string target_type;
DmTargetSnapshot::Status status;
- if (!QuerySnapshotStatus(name, &target_type, &status)) {
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
- }
- if (target_type == "snapshot" &&
- DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
- update_status.merge_phase() == MergePhase::FIRST_PHASE) {
- // The snapshot is not being merged because it's in the wrong phase.
- return MergeResult(UpdateState::None);
+ if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
+ return UpdateState::MergeFailed;
}
if (target_type != "snapshot-merge") {
// We can get here if we failed to rewrite the target type in
// InitiateMerge(). If we failed to create the target in first-stage
// init, boot would not succeed.
LOG(ERROR) << "Snapshot " << name << " has incorrect target type: " << target_type;
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ExpectedMergeTarget);
+ return UpdateState::MergeFailed;
}
// These two values are equal when merging is complete.
if (status.sectors_allocated != status.metadata_sectors) {
if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
- return MergeResult(UpdateState::MergeFailed,
- MergeFailureCode::UnmergedSectorsAfterCompletion);
+ return UpdateState::MergeFailed;
}
- return MergeResult(UpdateState::Merging);
- }
-
- auto code = CheckMergeConsistency(lock, name, snapshot_status);
- if (code != MergeFailureCode::Ok) {
- return MergeResult(UpdateState::MergeFailed, code);
+ return UpdateState::Merging;
}
// Merging is done. First, update the status file to indicate the merge
@@ -1141,132 +1006,12 @@ auto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string&
// snapshot device for this partition.
snapshot_status.set_state(SnapshotState::MERGE_COMPLETED);
if (!WriteSnapshotStatus(lock, snapshot_status)) {
- return MergeResult(UpdateState::MergeFailed, MergeFailureCode::WriteStatus);
+ return UpdateState::MergeFailed;
}
if (!OnSnapshotMergeComplete(lock, name, snapshot_status)) {
- return MergeResult(UpdateState::MergeNeedsReboot);
- }
- return MergeResult(UpdateState::MergeCompleted, MergeFailureCode::Ok);
-}
-
-// This returns the backing device, not the dm-user layer.
-static std::string GetMappedCowDeviceName(const std::string& snapshot,
- const SnapshotStatus& status) {
- // If no partition was created (the COW exists entirely on /data), the
- // device-mapper layering is different than if we had a partition.
- if (status.cow_partition_size() == 0) {
- return GetCowImageDeviceName(snapshot);
- }
- return GetCowName(snapshot);
-}
-
-MergeFailureCode SnapshotManager::CheckMergeConsistency(LockedFile* lock, const std::string& name,
- const SnapshotStatus& status) {
- CHECK(lock);
-
- if (!status.compression_enabled()) {
- // Do not try to verify old-style COWs yet.
- return MergeFailureCode::Ok;
- }
-
- auto& dm = DeviceMapper::Instance();
-
- std::string cow_image_name = GetMappedCowDeviceName(name, status);
- std::string cow_image_path;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
- LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
- return MergeFailureCode::GetCowPathConsistencyCheck;
- }
-
- // First pass, count # of ops.
- size_t num_ops = 0;
- {
- unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open " << cow_image_name;
- return MergeFailureCode::OpenCowConsistencyCheck;
- }
-
- CowReader reader;
- if (!reader.Parse(std::move(fd))) {
- LOG(ERROR) << "Failed to parse cow " << cow_image_path;
- return MergeFailureCode::ParseCowConsistencyCheck;
- }
-
- for (auto iter = reader.GetOpIter(); !iter->Done(); iter->Next()) {
- if (!IsMetadataOp(iter->Get())) {
- num_ops++;
- }
- }
- }
-
- // Second pass, try as hard as we can to get the actual number of blocks
- // the system thinks is merged.
- unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_DIRECT | O_SYNC | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open direct " << cow_image_name;
- return MergeFailureCode::OpenCowDirectConsistencyCheck;
- }
-
- void* addr;
- size_t page_size = getpagesize();
- if (posix_memalign(&addr, page_size, page_size) < 0) {
- PLOG(ERROR) << "posix_memalign with page size " << page_size;
- return MergeFailureCode::MemAlignConsistencyCheck;
- }
-
- // COWs are always at least 2MB, this is guaranteed in snapshot creation.
- std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
- if (!android::base::ReadFully(fd, buffer.get(), page_size)) {
- PLOG(ERROR) << "Direct read failed " << cow_image_name;
- return MergeFailureCode::DirectReadConsistencyCheck;
- }
-
- auto header = reinterpret_cast<CowHeader*>(buffer.get());
- if (header->num_merge_ops != num_ops) {
- LOG(ERROR) << "COW consistency check failed, expected " << num_ops << " to be merged, "
- << "but " << header->num_merge_ops << " were actually recorded.";
- LOG(ERROR) << "Aborting merge progress for snapshot " << name
- << ", will try again next boot";
- return MergeFailureCode::WrongMergeCountConsistencyCheck;
- }
-
- return MergeFailureCode::Ok;
-}
-
-MergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock, &snapshots)) {
- return MergeFailureCode::ListSnapshots;
- }
-
- SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- CHECK(update_status.state() == UpdateState::Merging);
- CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
-
- update_status.set_merge_phase(MergePhase::SECOND_PHASE);
- if (!WriteSnapshotUpdateStatus(lock, update_status)) {
- return MergeFailureCode::WriteStatus;
- }
-
- MergeFailureCode result = MergeFailureCode::Ok;
- for (const auto& snapshot : snapshots) {
- SnapshotStatus snapshot_status;
- if (!ReadSnapshotStatus(lock, snapshot, &snapshot_status)) {
- return MergeFailureCode::ReadStatus;
- }
- if (DecideMergePhase(snapshot_status) != MergePhase::SECOND_PHASE) {
- continue;
- }
- auto code = SwitchSnapshotToMerge(lock, snapshot);
- if (code != MergeFailureCode::Ok) {
- LOG(ERROR) << "Failed to switch snapshot to a second-phase merge target: " << snapshot;
- if (result == MergeFailureCode::Ok) {
- result = code;
- }
- }
+ return UpdateState::MergeNeedsReboot;
}
- return result;
+ return UpdateState::MergeCompleted;
}
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
@@ -1281,16 +1026,12 @@ std::string SnapshotManager::GetForwardMergeIndicatorPath() {
return metadata_dir_ + "/allow-forward-merge";
}
-std::string SnapshotManager::GetOldPartitionMetadataPath() {
- return metadata_dir_ + "/old-partition-metadata";
-}
-
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
// It's not possible to remove update state in recovery, so write an
// indicator that cleanup is needed on reboot. If a factory data reset
// was requested, it doesn't matter, everything will get wiped anyway.
// To make testing easier we consider a /data wipe as cleaned up.
- if (device_->IsRecovery()) {
+ if (device_->IsRecovery() && !in_factory_data_reset_) {
WriteUpdateState(lock, UpdateState::MergeCompleted);
return;
}
@@ -1298,7 +1039,7 @@ void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
RemoveAllUpdateState(lock);
}
-void SnapshotManager::AcknowledgeMergeFailure(MergeFailureCode failure_code) {
+void SnapshotManager::AcknowledgeMergeFailure() {
// Log first, so worst case, we always have a record of why the calls below
// were being made.
LOG(ERROR) << "Merge could not be completed and will be marked as failed.";
@@ -1315,25 +1056,26 @@ void SnapshotManager::AcknowledgeMergeFailure(MergeFailureCode failure_code) {
return;
}
- WriteUpdateState(lock.get(), UpdateState::MergeFailed, failure_code);
+ WriteUpdateState(lock.get(), UpdateState::MergeFailed);
}
bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
const SnapshotStatus& status) {
- if (IsSnapshotDevice(name)) {
+ auto dm_name = GetSnapshotDeviceName(name, status);
+ if (IsSnapshotDevice(dm_name)) {
// We are extra-cautious here, to avoid deleting the wrong table.
std::string target_type;
DmTargetSnapshot::Status dm_status;
- if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {
+ if (!QuerySnapshotStatus(dm_name, &target_type, &dm_status)) {
return false;
}
if (target_type != "snapshot-merge") {
LOG(ERROR) << "Unexpected target type " << target_type
- << " for snapshot device: " << name;
+ << " for snapshot device: " << dm_name;
return false;
}
if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
- LOG(ERROR) << "Merge is unexpectedly incomplete for device " << name;
+ LOG(ERROR) << "Merge is unexpectedly incomplete for device " << dm_name;
return false;
}
if (!CollapseSnapshotDevice(name, status)) {
@@ -1354,21 +1096,23 @@ bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::strin
bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
const SnapshotStatus& status) {
auto& dm = DeviceMapper::Instance();
+ auto dm_name = GetSnapshotDeviceName(name, status);
// Verify we have a snapshot-merge device.
DeviceMapper::TargetInfo target;
- if (!GetSingleTarget(name, TableQuery::Table, &target)) {
+ if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
return false;
}
if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
// This should be impossible, it was checked earlier.
- LOG(ERROR) << "Snapshot device has invalid target type: " << name;
+ LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
return false;
}
std::string base_device, cow_device;
if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
- LOG(ERROR) << "Could not parse snapshot device " << name << " parameters: " << target.data;
+ LOG(ERROR) << "Could not parse snapshot device " << dm_name
+ << " parameters: " << target.data;
return false;
}
@@ -1379,6 +1123,42 @@ bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
return false;
}
+ if (dm_name != name) {
+ // We've derived the base device, but we actually need to replace the
+ // table of the outermost device. Do a quick verification that this
+ // device looks like we expect it to.
+ std::vector<DeviceMapper::TargetInfo> outer_table;
+ if (!dm.GetTableInfo(name, &outer_table)) {
+ LOG(ERROR) << "Could not validate outer snapshot table: " << name;
+ return false;
+ }
+ if (outer_table.size() != 2) {
+ LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
+ << ", got: " << outer_table.size();
+ return false;
+ }
+ for (const auto& target : outer_table) {
+ auto target_type = DeviceMapper::GetTargetType(target.spec);
+ if (target_type != "linear") {
+ LOG(ERROR) << "Outer snapshot table may only contain linear targets, but " << name
+ << " has target: " << target_type;
+ return false;
+ }
+ }
+ if (outer_table[0].spec.length != snapshot_sectors) {
+ LOG(ERROR) << "dm-snapshot " << name << " should have " << snapshot_sectors
+ << " sectors, got: " << outer_table[0].spec.length;
+ return false;
+ }
+ uint64_t expected_device_sectors = status.device_size() / kSectorSize;
+ uint64_t actual_device_sectors = outer_table[0].spec.length + outer_table[1].spec.length;
+ if (expected_device_sectors != actual_device_sectors) {
+ LOG(ERROR) << "Outer device " << name << " should have " << expected_device_sectors
+ << " sectors, got: " << actual_device_sectors;
+ return false;
+ }
+ }
+
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
// Create a DmTable that is identical to the base device.
CreateLogicalPartitionParams base_device_params{
@@ -1393,6 +1173,7 @@ bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
return false;
}
+ // Note: we are replacing the *outer* table here, so we do not use dm_name.
if (!dm.LoadTableAndActivate(name, table)) {
return false;
}
@@ -1402,18 +1183,18 @@ bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
// flushed remaining I/O. We could in theory replace with dm-zero (or
// re-use the table above), but for now it's better to know why this
// would fail.
- if (status.compression_enabled()) {
- UnmapDmUserDevice(name);
+ if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
+ LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
+ << "reclaimed until after reboot.";
+ return false;
}
+
+ // Cleanup the base device as well, since it is no longer used. This does
+ // not block cleanup.
auto base_name = GetBaseDeviceName(name);
- if (!DeleteDeviceIfExists(base_name)) {
+ if (!dm.DeleteDeviceIfExists(base_name)) {
LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
}
-
- if (!DeleteDeviceIfExists(GetSourceDeviceName(name), 4000ms)) {
- LOG(ERROR) << "Unable to delete source device for snapshot: " << GetSourceDeviceName(name);
- }
-
return true;
}
@@ -1452,126 +1233,6 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
return RemoveAllUpdateState(lock, before_cancel);
}
-bool SnapshotManager::PerformInitTransition(InitTransition transition,
- std::vector<std::string>* snapuserd_argv) {
- LOG(INFO) << "Performing transition for snapuserd.";
-
- // Don't use EnsuerSnapuserdConnected() because this is called from init,
- // and attempting to do so will deadlock.
- if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
- snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
- if (!snapuserd_client_) {
- LOG(ERROR) << "Unable to connect to snapuserd";
- return false;
- }
- }
-
- auto& dm = DeviceMapper::Instance();
-
- auto lock = LockExclusive();
- if (!lock) return false;
-
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock.get(), &snapshots)) {
- LOG(ERROR) << "Failed to list snapshots.";
- return false;
- }
-
- size_t num_cows = 0;
- size_t ok_cows = 0;
- for (const auto& snapshot : snapshots) {
- std::string user_cow_name = GetDmUserCowName(snapshot);
- if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
- continue;
- }
-
- DeviceMapper::TargetInfo target;
- if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {
- continue;
- }
-
- auto target_type = DeviceMapper::GetTargetType(target.spec);
- if (target_type != "user") {
- LOG(ERROR) << "Unexpected target type for " << user_cow_name << ": " << target_type;
- continue;
- }
-
- num_cows++;
-
- SnapshotStatus snapshot_status;
- if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
- LOG(ERROR) << "Unable to read snapshot status: " << snapshot;
- continue;
- }
-
- auto misc_name = user_cow_name;
-
- DmTable table;
- table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
- if (!dm.LoadTableAndActivate(user_cow_name, table)) {
- LOG(ERROR) << "Unable to swap tables for " << misc_name;
- continue;
- }
-
- std::string source_device;
- if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
- LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
- continue;
- }
-
- std::string cow_image_name = GetMappedCowDeviceName(snapshot, snapshot_status);
-
- std::string cow_image_device;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
- LOG(ERROR) << "Could not get device path for " << cow_image_name;
- continue;
- }
-
- // Wait for ueventd to acknowledge and create the control device node.
- std::string control_device = "/dev/dm-user/" + misc_name;
- if (!WaitForDevice(control_device, 10s)) {
- LOG(ERROR) << "dm-user control device no found: " << misc_name;
- continue;
- }
-
- if (transition == InitTransition::SELINUX_DETACH) {
- auto message = misc_name + "," + cow_image_device + "," + source_device;
- snapuserd_argv->emplace_back(std::move(message));
-
- // Do not attempt to connect to the new snapuserd yet, it hasn't
- // been started. We do however want to wait for the misc device
- // to have been created.
- ok_cows++;
- continue;
- }
-
- uint64_t base_sectors =
- snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);
- if (base_sectors == 0) {
- // Unrecoverable as metadata reads from cow device failed
- LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
- return false;
- }
-
- CHECK(base_sectors <= target.spec.length);
-
- if (!snapuserd_client_->AttachDmUser(misc_name)) {
- // This error is unrecoverable. We cannot proceed because reads to
- // the underlying device will fail.
- LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
- return false;
- }
-
- ok_cows++;
- }
-
- if (ok_cows != num_cows) {
- LOG(ERROR) << "Could not transition all snapuserd consumers.";
- return false;
- }
- return true;
-}
-
std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
const auto& opener = device_->GetPartitionOpener();
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
@@ -1691,19 +1352,7 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
// Otherwise (UPDATED flag), only delete snapshots if they are not mapped
// as dm-snapshot (for example, after merge completes).
bool should_unmap = current_slot != Slot::Target;
- bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name);
- if (should_unmap && android::base::EndsWith(name, device_->GetSlotSuffix())) {
- // Something very unexpected has happened - we want to unmap this
- // snapshot, but it's on the wrong slot. We can't unmap an active
- // partition. If this is not really a snapshot, skip the unmap
- // step.
- auto& dm = DeviceMapper::Instance();
- if (dm.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
- LOG(ERROR) << "Detected snapshot " << name << " on " << current_slot << " slot"
- << " for source partition; removing without unmap.";
- should_unmap = false;
- }
- }
+ bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
bool partition_ok = true;
if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
@@ -1737,7 +1386,8 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
}
// See comments in RemoveAllSnapshots().
-bool SnapshotManager::ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status,
+bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
+ const std::map<std::string, bool>& flashing_status,
Slot current_slot, const std::string& name) {
if (current_slot != Slot::Target) {
return true;
@@ -1751,7 +1401,16 @@ bool SnapshotManager::ShouldDeleteSnapshot(const std::map<std::string, bool>& fl
// partition flashed, okay to delete obsolete snapshots
return true;
}
- return !IsSnapshotDevice(name);
+ // partition updated, only delete if not dm-snapshot
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock, name, &status)) {
+ LOG(WARNING) << "Unable to read snapshot status for " << name
+ << ", guessing snapshot device name";
+ auto extra_name = GetSnapshotExtraDeviceName(name);
+ return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
+ }
+ auto dm_name = GetSnapshotDeviceName(name, status);
+ return !IsSnapshotDevice(dm_name);
}
UpdateState SnapshotManager::GetUpdateState(double* progress) {
@@ -1795,7 +1454,6 @@ UpdateState SnapshotManager::GetUpdateState(double* progress) {
for (const auto& snapshot : snapshots) {
DmTargetSnapshot::Status current_status;
- if (!IsSnapshotDevice(snapshot)) continue;
if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
@@ -1809,19 +1467,7 @@ UpdateState SnapshotManager::GetUpdateState(double* progress) {
return state;
}
-bool SnapshotManager::UpdateUsesCompression() {
- auto lock = LockShared();
- if (!lock) return false;
- return UpdateUsesCompression(lock.get());
-}
-
-bool SnapshotManager::UpdateUsesCompression(LockedFile* lock) {
- SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- return update_status.compression_enabled();
-}
-
-bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
- const std::string& suffix) {
+bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots) {
CHECK(lock);
auto dir_path = metadata_dir_ + "/snapshots"s;
@@ -1834,12 +1480,7 @@ bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>*
struct dirent* dp;
while ((dp = readdir(dir.get())) != nullptr) {
if (dp->d_type != DT_REG) continue;
-
- std::string name(dp->d_name);
- if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
- continue;
- }
- snapshots->emplace_back(std::move(name));
+ snapshots->emplace_back(dp->d_name);
}
return true;
}
@@ -1901,23 +1542,14 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(
auto lock = LockExclusive();
if (!lock) return false;
- uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
- return MapAllPartitions(lock.get(), super_device, slot, timeout_ms);
-}
-
-bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& super_device,
- uint32_t slot, const std::chrono::milliseconds& timeout_ms) {
const auto& opener = device_->GetPartitionOpener();
+ uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
if (!metadata) {
LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
return false;
}
- if (!EnsureImageManager()) {
- return false;
- }
-
for (const auto& partition : metadata->partitions) {
if (GetPartitionGroupName(metadata->groups[partition.group_index]) == kCowGroupName) {
LOG(INFO) << "Skip mapping partition " << GetPartitionName(partition) << " in group "
@@ -1932,7 +1564,8 @@ bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& supe
.partition_opener = &opener,
.timeout_ms = timeout_ms,
};
- if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) {
+ std::string ignore_path;
+ if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
return false;
}
}
@@ -1960,10 +1593,11 @@ static std::chrono::milliseconds GetRemainingTime(
bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
CreateLogicalPartitionParams params,
- SnapshotContext context, SnapshotPaths* paths) {
+ std::string* path) {
auto begin = std::chrono::steady_clock::now();
CHECK(lock);
+ path->clear();
if (params.GetPartitionName() != params.GetDeviceName()) {
LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
@@ -2044,11 +1678,8 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
}
created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
- if (paths) {
- paths->target_device = base_path;
- }
-
if (!live_snapshot_status.has_value()) {
+ *path = base_path;
created_devices.Release();
return true;
}
@@ -2075,76 +1706,21 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
return false;
}
- if (paths) {
- paths->cow_device_name = cow_name;
- }
remaining_time = GetRemainingTime(params.timeout_ms, begin);
if (remaining_time.count() < 0) return false;
- if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
- // Stop here, we can't run dm-user yet, the COW isn't built.
- created_devices.Release();
- return true;
- }
-
- if (live_snapshot_status->compression_enabled()) {
- // Get the source device (eg the view of the partition from before it was resized).
- std::string source_device_path;
- if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
- &source_device_path)) {
- LOG(ERROR) << "Could not map source device for: " << cow_name;
- return false;
- }
-
- auto source_device = GetSourceDeviceName(params.GetPartitionName());
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
-
- if (!WaitForDevice(source_device_path, remaining_time)) {
- return false;
- }
-
- std::string cow_path;
- if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
- LOG(ERROR) << "Could not determine path for: " << cow_name;
- return false;
- }
- if (!WaitForDevice(cow_path, remaining_time)) {
- return false;
- }
-
- auto name = GetDmUserCowName(params.GetPartitionName());
-
- std::string new_cow_device;
- if (!MapDmUserCow(lock, name, cow_path, source_device_path, remaining_time,
- &new_cow_device)) {
- LOG(ERROR) << "Could not map dm-user device for partition "
- << params.GetPartitionName();
- return false;
- }
- created_devices.EmplaceBack<AutoUnmapDevice>(&dm, name);
-
- remaining_time = GetRemainingTime(params.timeout_ms, begin);
- if (remaining_time.count() < 0) return false;
-
- cow_device = new_cow_device;
- }
-
- std::string path;
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
- &path)) {
+ path)) {
LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
return false;
}
// No need to add params.GetPartitionName() to created_devices since it is immediately released.
- if (paths) {
- paths->snapshot_device = path;
- }
-
created_devices.Release();
- LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << path;
+ LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
+
return true;
}
@@ -2160,18 +1736,13 @@ bool SnapshotManager::UnmapPartitionWithSnapshot(LockedFile* lock,
return false;
}
- auto base_name = GetBaseDeviceName(target_partition_name);
- if (!DeleteDeviceIfExists(base_name)) {
+ auto& dm = DeviceMapper::Instance();
+ std::string base_name = GetBaseDeviceName(target_partition_name);
+ if (!dm.DeleteDeviceIfExists(base_name)) {
LOG(ERROR) << "Cannot delete base device: " << base_name;
return false;
}
- auto source_name = GetSourceDeviceName(target_partition_name);
- if (!DeleteDeviceIfExists(source_name)) {
- LOG(ERROR) << "Cannot delete source device: " << source_name;
- return false;
- }
-
LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
return true;
@@ -2253,12 +1824,10 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
CHECK(lock);
if (!EnsureImageManager()) return false;
- if (UpdateUsesCompression(lock) && !UnmapDmUserDevice(name)) {
- return false;
- }
-
- if (!DeleteDeviceIfExists(GetCowName(name), 4000ms)) {
- LOG(ERROR) << "Cannot unmap: " << GetCowName(name);
+ auto& dm = DeviceMapper::Instance();
+ auto cow_name = GetCowName(name);
+ if (!dm.DeleteDeviceIfExists(cow_name)) {
+ LOG(ERROR) << "Cannot unmap " << cow_name;
return false;
}
@@ -2270,123 +1839,6 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
return true;
}
-bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) {
- auto& dm = DeviceMapper::Instance();
-
- auto dm_user_name = GetDmUserCowName(snapshot_name);
- if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) {
- return true;
- }
-
- if (!DeleteDeviceIfExists(dm_user_name)) {
- LOG(ERROR) << "Cannot unmap " << dm_user_name;
- return false;
- }
-
- if (EnsureSnapuserdConnected()) {
- if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
- LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
- return false;
- }
- }
-
- // Ensure the control device is gone so we don't run into ABA problems.
- auto control_device = "/dev/dm-user/" + dm_user_name;
- if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
- LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
- return false;
- }
- return true;
-}
-
-bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) {
- auto lock = LockExclusive();
- if (!lock) return false;
-
- auto state = ReadUpdateState(lock.get());
- if (state == UpdateState::Unverified) {
- if (GetCurrentSlot() == Slot::Target) {
- LOG(ERROR) << "Cannot call MapAllSnapshots when booting from the target slot.";
- return false;
- }
- } else if (state != UpdateState::Initiated) {
- LOG(ERROR) << "Cannot call MapAllSnapshots from update state: " << state;
- return false;
- }
-
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock.get(), &snapshots)) {
- return false;
- }
-
- const auto& opener = device_->GetPartitionOpener();
- auto slot_suffix = device_->GetOtherSlotSuffix();
- auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
- auto super_device = device_->GetSuperDevice(slot_number);
- auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot_number);
- if (!metadata) {
- LOG(ERROR) << "MapAllSnapshots could not read dynamic partition metadata for device: "
- << super_device;
- return false;
- }
-
- for (const auto& snapshot : snapshots) {
- if (!UnmapPartitionWithSnapshot(lock.get(), snapshot)) {
- LOG(ERROR) << "MapAllSnapshots could not unmap snapshot: " << snapshot;
- return false;
- }
-
- CreateLogicalPartitionParams params = {
- .block_device = super_device,
- .metadata = metadata.get(),
- .partition_name = snapshot,
- .partition_opener = &opener,
- .timeout_ms = timeout_ms,
- };
- if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
- nullptr)) {
- LOG(ERROR) << "MapAllSnapshots failed to map: " << snapshot;
- return false;
- }
- }
-
- LOG(INFO) << "MapAllSnapshots succeeded.";
- return true;
-}
-
-bool SnapshotManager::UnmapAllSnapshots() {
- auto lock = LockExclusive();
- if (!lock) return false;
-
- return UnmapAllSnapshots(lock.get());
-}
-
-bool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock, &snapshots)) {
- return false;
- }
-
- for (const auto& snapshot : snapshots) {
- if (!UnmapPartitionWithSnapshot(lock, snapshot)) {
- LOG(ERROR) << "Failed to unmap snapshot: " << snapshot;
- return false;
- }
- }
-
- // Terminate the daemon and release the snapuserd_client_ object.
- // If we need to re-connect with the daemon, EnsureSnapuserdConnected()
- // will re-create the object and establish the socket connection.
- if (snapuserd_client_) {
- LOG(INFO) << "Shutdown snapuserd daemon";
- snapuserd_client_->DetachSnapuserd();
- snapuserd_client_->CloseConnection();
- snapuserd_client_ = nullptr;
- }
-
- return true;
-}
-
auto SnapshotManager::OpenFile(const std::string& file, int lock_flags)
-> std::unique_ptr<LockedFile> {
unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
@@ -2510,31 +1962,9 @@ SnapshotUpdateStatus SnapshotManager::ReadSnapshotUpdateStatus(LockedFile* lock)
return status;
}
-bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state,
- MergeFailureCode failure_code) {
- SnapshotUpdateStatus status;
+bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
+ SnapshotUpdateStatus status = {};
status.set_state(state);
-
- switch (state) {
- case UpdateState::MergeFailed:
- status.set_merge_failure_code(failure_code);
- break;
- case UpdateState::Initiated:
- status.set_source_build_fingerprint(
- android::base::GetProperty("ro.build.fingerprint", ""));
- break;
- default:
- break;
- }
-
- // If we're transitioning between two valid states (eg, we're not beginning
- // or ending an OTA), then make sure to propagate the compression bit and
- // build fingerprint.
- if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
- SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
- status.set_compression_enabled(old_status.compression_enabled());
- status.set_source_build_fingerprint(old_status.source_build_fingerprint());
- }
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -2645,10 +2075,19 @@ bool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus
return true;
}
+std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
+ const SnapshotStatus& status) {
+ if (status.device_size() != status.snapshot_size()) {
+ return GetSnapshotExtraDeviceName(snapshot_name);
+ }
+ return snapshot_name;
+}
+
bool SnapshotManager::EnsureImageManager() {
if (images_) return true;
- images_ = device_->OpenImageManager();
+ // For now, use a preset timeout.
+ images_ = android::fiemap::IImageManager::Open(gsid_dir_, 15000ms);
if (!images_) {
LOG(ERROR) << "Could not open ImageManager";
return false;
@@ -2656,27 +2095,21 @@ bool SnapshotManager::EnsureImageManager() {
return true;
}
-bool SnapshotManager::EnsureSnapuserdConnected() {
- if (snapuserd_client_) {
- return true;
- }
-
- if (!use_first_stage_snapuserd_ && !EnsureSnapuserdStarted()) {
- return false;
- }
-
- snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
- if (!snapuserd_client_) {
- LOG(ERROR) << "Unable to connect to snapuserd";
+bool SnapshotManager::ForceLocalImageManager() {
+ images_ = android::fiemap::ImageManager::Open(gsid_dir_);
+ if (!images_) {
+ LOG(ERROR) << "Could not open ImageManager";
return false;
}
+ has_local_image_manager_ = true;
return true;
}
-void SnapshotManager::UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
+static void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
+ auto& dm = DeviceMapper::Instance();
std::vector<std::string> to_delete;
for (auto* existing_cow_partition : current_metadata->ListPartitionsInGroup(kCowGroupName)) {
- if (!DeleteDeviceIfExists(existing_cow_partition->name())) {
+ if (!dm.DeleteDeviceIfExists(existing_cow_partition->name())) {
LOG(WARNING) << existing_cow_partition->name()
<< " cannot be unmapped and its space cannot be reclaimed";
continue;
@@ -2704,12 +2137,6 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
auto lock = LockExclusive();
if (!lock) return Return::Error();
- auto update_state = ReadUpdateState(lock.get());
- if (update_state != UpdateState::Initiated) {
- LOG(ERROR) << "Cannot create update snapshots in state " << update_state;
- return Return::Error();
- }
-
// TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
// partition takes up a big chunk of space in super, causing COW images to be created on
// retrofit Virtual A/B devices.
@@ -2772,41 +2199,14 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
// these devices.
AutoDeviceList created_devices;
- const auto& dap_metadata = manifest.dynamic_partition_metadata();
- CowOptions options;
- CowWriter writer(options);
- bool cow_format_support = true;
- if (dap_metadata.cow_version() < writer.GetCowVersion()) {
- cow_format_support = false;
- }
-
- LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
- << " writer.GetCowVersion(): " << writer.GetCowVersion();
-
- bool use_compression = IsCompressionEnabled() && dap_metadata.vabc_enabled() &&
- !device_->IsRecovery() && cow_format_support;
-
- std::string compression_algorithm;
- if (use_compression) {
- compression_algorithm = dap_metadata.vabc_compression_param();
- if (compression_algorithm.empty()) {
- // Older OTAs don't set an explicit compression type, so default to gz.
- compression_algorithm = "gz";
- }
- } else {
- compression_algorithm = "none";
- }
-
PartitionCowCreator cow_creator{
.target_metadata = target_metadata.get(),
.target_suffix = target_suffix,
.target_partition = nullptr,
.current_metadata = current_metadata.get(),
.current_suffix = current_suffix,
- .update = nullptr,
+ .operations = nullptr,
.extra_extents = {},
- .compression_enabled = use_compression,
- .compression_algorithm = compression_algorithm,
};
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
@@ -2830,32 +2230,6 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
return Return::Error();
}
- // If compression is enabled, we need to retain a copy of the old metadata
- // so we can access original blocks in case they are moved around. We do
- // not want to rely on the old super metadata slot because we don't
- // guarantee its validity after the slot switch is successful.
- if (cow_creator.compression_enabled) {
- auto metadata = current_metadata->Export();
- if (!metadata) {
- LOG(ERROR) << "Could not export current metadata";
- return Return::Error();
- }
-
- auto path = GetOldPartitionMetadataPath();
- if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {
- LOG(ERROR) << "Cannot write old metadata to " << path;
- return Return::Error();
- }
- }
-
- SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
- status.set_state(update_state);
- status.set_compression_enabled(cow_creator.compression_enabled);
- if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
- LOG(ERROR) << "Unable to write new update state";
- return Return::Error();
- }
-
created_devices.Release();
LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
@@ -2876,11 +2250,12 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
return Return::Error();
}
- std::map<std::string, const PartitionUpdate*> partition_map;
+ std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
std::map<std::string, std::vector<Extent>> extra_extents_map;
for (const auto& partition_update : manifest.partitions()) {
auto suffixed_name = partition_update.partition_name() + target_suffix;
- auto&& [it, inserted] = partition_map.emplace(suffixed_name, &partition_update);
+ auto&& [it, inserted] =
+ install_operation_map.emplace(suffixed_name, &partition_update.operations());
if (!inserted) {
LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
<< " in update manifest.";
@@ -2898,14 +2273,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
cow_creator->target_partition = target_partition;
- cow_creator->update = nullptr;
- auto iter = partition_map.find(target_partition->name());
- if (iter != partition_map.end()) {
- cow_creator->update = iter->second;
- } else {
- LOG(INFO) << target_partition->name()
- << " isn't included in the payload, skipping the cow creation.";
- continue;
+ cow_creator->operations = nullptr;
+ auto operations_it = install_operation_map.find(target_partition->name());
+ if (operations_it != install_operation_map.end()) {
+ cow_creator->operations = operations_it->second;
}
cow_creator->extra_extents.clear();
@@ -2917,7 +2288,6 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
// Compute the device sizes for the partition.
auto cow_creator_ret = cow_creator->Run();
if (!cow_creator_ret.has_value()) {
- LOG(ERROR) << "PartitionCowCreator returned no value for " << target_partition->name();
return Return::Error();
}
@@ -2948,17 +2318,8 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
continue;
}
- // Find the original partition size.
- auto name = target_partition->name();
- auto old_partition_name =
- name.substr(0, name.size() - target_suffix.size()) + cow_creator->current_suffix;
- auto old_partition = cow_creator->current_metadata->FindPartition(old_partition_name);
- if (old_partition) {
- cow_creator_ret->snapshot_status.set_old_partition_size(old_partition->size());
- }
-
// Store these device sizes to snapshot status file.
- if (!CreateSnapshot(lock, cow_creator, &cow_creator_ret->snapshot_status)) {
+ if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
return Return::Error();
}
created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
@@ -3043,32 +2404,11 @@ Return SnapshotManager::InitializeUpdateSnapshots(
return Return::Error();
}
- if (it->second.compression_enabled()) {
- unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
- << cow_params.partition_name;
- return Return::Error();
- }
-
- CowOptions options;
- if (device()->IsTestDevice()) {
- options.scratch_space = false;
- }
- options.compression = it->second.compression_algorithm();
-
- CowWriter writer(options);
- if (!writer.Initialize(fd) || !writer.Finalize()) {
- LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
- return Return::Error();
- }
- } else {
- auto ret = InitializeKernelCow(cow_path);
- if (!ret.is_ok()) {
- LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
- << cow_path;
- return AddRequiredSpace(ret, all_snapshot_status);
- }
+ auto ret = InitializeCow(cow_path);
+ if (!ret.is_ok()) {
+ LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
+ << cow_path;
+ return AddRequiredSpace(ret, all_snapshot_status);
}
// Let destructor of created_devices_for_cow to unmap the COW devices.
};
@@ -3084,157 +2424,16 @@ bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& para
<< params.GetPartitionName();
return false;
}
-
- SnapshotStatus status;
- if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
- return false;
- }
- if (status.compression_enabled()) {
- LOG(ERROR) << "Cannot use MapUpdateSnapshot with compressed snapshots";
- return false;
- }
-
- SnapshotPaths paths;
- if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
- return false;
- }
-
- if (!paths.snapshot_device.empty()) {
- *snapshot_path = paths.snapshot_device;
- } else {
- *snapshot_path = paths.target_device;
- }
- DCHECK(!snapshot_path->empty());
- return true;
+ return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
}
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
- const android::fs_mgr::CreateLogicalPartitionParams& params,
- const std::optional<std::string>& source_device) {
-#if defined(LIBSNAPSHOT_NO_COW_WRITE)
- (void)params;
- (void)source_device;
-
- LOG(ERROR) << "Snapshots cannot be written in first-stage init or recovery";
- return nullptr;
-#else
- // First unmap any existing mapping.
- auto lock = LockShared();
- if (!lock) return nullptr;
- if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
- LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
- << params.GetPartitionName();
- return nullptr;
- }
-
- SnapshotPaths paths;
- if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {
- return nullptr;
- }
-
- SnapshotStatus status;
- if (!paths.cow_device_name.empty()) {
- if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
- return nullptr;
- }
- } else {
- // Currently, partition_cow_creator always creates snapshots. The
- // reason is that if partition X shrinks while partition Y grows, we
- // cannot bindly write to the newly freed extents in X. This would
- // make the old slot unusable. So, the entire size of the target
- // partition is currently considered snapshottable.
- LOG(ERROR) << "No snapshot available for partition " << params.GetPartitionName();
- return nullptr;
- }
-
- if (status.compression_enabled()) {
- return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
- status, paths);
- }
- return OpenKernelSnapshotWriter(lock.get(), source_device, params.GetPartitionName(), status,
- paths);
-#endif
-}
-
-#if !defined(LIBSNAPSHOT_NO_COW_WRITE)
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths) {
- CHECK(lock);
-
- CowOptions cow_options;
- cow_options.compression = status.compression_algorithm();
- cow_options.max_blocks = {status.device_size() / cow_options.block_size};
- // Disable scratch space for vts tests
- if (device()->IsTestDevice()) {
- cow_options.scratch_space = false;
- }
-
- // Currently we don't support partial snapshots, since partition_cow_creator
- // never creates this scenario.
- CHECK(status.snapshot_size() == status.device_size());
-
- auto writer = std::make_unique<CompressedSnapshotWriter>(cow_options);
- if (source_device) {
- writer->SetSourceDevice(*source_device);
- }
-
- std::string cow_path;
- if (!GetMappedImageDevicePath(paths.cow_device_name, &cow_path)) {
- LOG(ERROR) << "Could not determine path for " << paths.cow_device_name;
- return nullptr;
- }
-
- unique_fd cow_fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
- if (cow_fd < 0) {
- PLOG(ERROR) << "OpenCompressedSnapshotWriter: open " << cow_path;
- return nullptr;
- }
- if (!writer->SetCowDevice(std::move(cow_fd))) {
- LOG(ERROR) << "Could not create COW writer from " << cow_path;
- return nullptr;
- }
-
- return writer;
-}
-
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths) {
- CHECK(lock);
-
- CowOptions cow_options;
- cow_options.max_blocks = {status.device_size() / cow_options.block_size};
-
- auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
-
- std::string path = paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
- unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << path;
- return nullptr;
- }
-
- if (source_device) {
- writer->SetSourceDevice(*source_device);
- }
-
- uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
- writer->SetSnapshotDevice(std::move(fd), cow_size);
-
- return writer;
-}
-#endif // !defined(LIBSNAPSHOT_NO_COW_WRITE)
-
bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
auto lock = LockShared();
if (!lock) return false;
return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
}
-bool SnapshotManager::UnmapAllPartitionsInRecovery() {
+bool SnapshotManager::UnmapAllPartitions() {
auto lock = LockExclusive();
if (!lock) return false;
@@ -3274,10 +2473,8 @@ bool SnapshotManager::Dump(std::ostream& os) {
std::stringstream ss;
- auto update_status = ReadSnapshotUpdateStatus(file.get());
-
ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
- ss << "Compression: " << update_status.compression_enabled() << std::endl;
+
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
ss << "Rollback indicator: "
@@ -3286,7 +2483,6 @@ bool SnapshotManager::Dump(std::ostream& os) {
ss << "Forward merge indicator: "
<< (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
<< std::endl;
- ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl;
bool ok = true;
std::vector<std::string> snapshots;
@@ -3309,7 +2505,6 @@ bool SnapshotManager::Dump(std::ostream& os) {
ss << " cow file size (bytes): " << status.cow_file_size() << std::endl;
ss << " allocated sectors: " << status.sectors_allocated() << std::endl;
ss << " metadata sectors: " << status.metadata_sectors() << std::endl;
- ss << " compression: " << status.compression_algorithm() << std::endl;
}
os << ss.rdbuf();
return ok;
@@ -3361,7 +2556,7 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto super_path = device_->GetSuperDevice(slot_number);
- if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
LOG(ERROR) << "Unable to map partitions to complete merge.";
return false;
}
@@ -3374,28 +2569,17 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
};
in_factory_data_reset_ = true;
- UpdateState state =
- ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+ bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
in_factory_data_reset_ = false;
- if (state == UpdateState::MergeFailed) {
+ if (!ok) {
return false;
}
// Nothing should be depending on partitions now, so unmap them all.
- if (!UnmapAllPartitionsInRecovery()) {
+ if (!UnmapAllPartitions()) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
}
-
- if (state != UpdateState::None) {
- auto lock = LockExclusive();
- if (!lock) return false;
-
- // Zap the update state so the bootloader doesn't think we're still
- // merging. It's okay if this fails, it's informative only at this
- // point.
- WriteUpdateState(lock.get(), UpdateState::None);
- }
return true;
}
@@ -3412,7 +2596,7 @@ bool SnapshotManager::FinishMergeInRecovery() {
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto super_path = device_->GetSuperDevice(slot_number);
- if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
LOG(ERROR) << "Unable to map partitions to complete merge.";
return false;
}
@@ -3424,21 +2608,21 @@ bool SnapshotManager::FinishMergeInRecovery() {
}
// Nothing should be depending on partitions now, so unmap them all.
- if (!UnmapAllPartitionsInRecovery()) {
+ if (!UnmapAllPartitions()) {
LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
}
return true;
}
-UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
- const std::function<bool()>& callback) {
+bool SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+ const std::function<bool()>& callback) {
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
UpdateState state = ProcessUpdateState(callback);
LOG(INFO) << "Update state in recovery: " << state;
switch (state) {
case UpdateState::MergeFailed:
LOG(ERROR) << "Unrecoverable merge failure detected.";
- return state;
+ return false;
case UpdateState::Unverified: {
// If an OTA was just applied but has not yet started merging:
//
@@ -3458,12 +2642,8 @@ UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_mer
if (allow_forward_merge &&
access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
LOG(INFO) << "Forward merge allowed, initiating merge now.";
-
- if (!InitiateMerge()) {
- LOG(ERROR) << "Failed to initiate merge on data wipe.";
- return UpdateState::MergeFailed;
- }
- return ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
+ return InitiateMerge() &&
+ ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
}
LOG(ERROR) << "Reverting to old slot since update will be deleted.";
@@ -3481,7 +2661,7 @@ UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_mer
default:
break;
}
- return state;
+ return true;
}
bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
@@ -3495,14 +2675,6 @@ bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
- SnapshotStatus status;
- if (!ReadSnapshotStatus(lock, snapshot, &status)) {
- return false;
- }
- if (status.compression_enabled()) {
- continue;
- }
-
std::vector<DeviceMapper::TargetInfo> targets;
if (!dm.GetTableStatus(snapshot, &targets)) {
LOG(ERROR) << "Could not read snapshot device table: " << snapshot;
@@ -3562,7 +2734,7 @@ CreateResult SnapshotManager::RecoveryCreateSnapshotDevices(
auto slot_suffix = device_->GetOtherSlotSuffix();
auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
auto super_path = device_->GetSuperDevice(slot_number);
- if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
LOG(ERROR) << "Unable to map partitions.";
return CreateResult::ERROR;
}
@@ -3588,26 +2760,6 @@ bool SnapshotManager::UpdateForwardMergeIndicator(bool wipe) {
return true;
}
-ISnapshotMergeStats* SnapshotManager::GetSnapshotMergeStatsInstance() {
- return SnapshotMergeStats::GetInstance(*this);
-}
-
-// This is only to be used in recovery or normal Android (not first-stage init).
-// We don't guarantee dm paths are available in first-stage init, because ueventd
-// isn't running yet.
-bool SnapshotManager::GetMappedImageDevicePath(const std::string& device_name,
- std::string* device_path) {
- auto& dm = DeviceMapper::Instance();
-
- // Try getting the device string if it is a device mapper device.
- if (dm.GetState(device_name) != DmDeviceState::INVALID) {
- return dm.GetDmDevicePathByName(device_name, device_path);
- }
-
- // Otherwise, get path from IImageManager.
- return images_->GetMappedImageDevice(device_name, device_path);
-}
-
bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
std::string* device_string_or_mapped_path) {
auto& dm = DeviceMapper::Instance();
@@ -3627,190 +2779,5 @@ bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device
return true;
}
-bool SnapshotManager::WaitForDevice(const std::string& device,
- std::chrono::milliseconds timeout_ms) {
- if (!android::base::StartsWith(device, "/")) {
- return true;
- }
-
- // In first-stage init, we rely on init setting a callback which can
- // regenerate uevents and populate /dev for us.
- if (uevent_regen_callback_) {
- if (!uevent_regen_callback_(device)) {
- LOG(ERROR) << "Failed to find device after regenerating uevents: " << device;
- return false;
- }
- return true;
- }
-
- // Otherwise, the only kind of device we need to wait for is a dm-user
- // misc device. Normal calls to DeviceMapper::CreateDevice() guarantee
- // the path has been created.
- if (!android::base::StartsWith(device, "/dev/dm-user/")) {
- return true;
- }
-
- if (timeout_ms.count() == 0) {
- LOG(ERROR) << "No timeout was specified to wait for device: " << device;
- return false;
- }
- if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {
- LOG(ERROR) << "Timed out waiting for device to appear: " << device;
- return false;
- }
- return true;
-}
-
-bool SnapshotManager::IsSnapuserdRequired() {
- auto lock = LockExclusive();
- if (!lock) return false;
-
- auto status = ReadSnapshotUpdateStatus(lock.get());
- return status.state() != UpdateState::None && status.compression_enabled();
-}
-
-bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
- return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);
-}
-
-bool SnapshotManager::PerformSecondStageInitTransition() {
- return PerformInitTransition(InitTransition::SECOND_STAGE);
-}
-
-const LpMetadata* SnapshotManager::ReadOldPartitionMetadata(LockedFile* lock) {
- CHECK(lock);
-
- if (!old_partition_metadata_) {
- auto path = GetOldPartitionMetadataPath();
- old_partition_metadata_ = android::fs_mgr::ReadFromImageFile(path);
- if (!old_partition_metadata_) {
- LOG(ERROR) << "Could not read old partition metadata from " << path;
- return nullptr;
- }
- }
- return old_partition_metadata_.get();
-}
-
-MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
- if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
- return MergePhase::FIRST_PHASE;
- }
- return MergePhase::SECOND_PHASE;
-}
-
-void SnapshotManager::UpdateCowStats(ISnapshotMergeStats* stats) {
- auto lock = LockExclusive();
- if (!lock) return;
-
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock.get(), &snapshots, GetSnapshotSlotSuffix())) {
- LOG(ERROR) << "Could not list snapshots";
- return;
- }
-
- uint64_t cow_file_size = 0;
- uint64_t total_cow_size = 0;
- uint64_t estimated_cow_size = 0;
- for (const auto& snapshot : snapshots) {
- SnapshotStatus status;
- if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
- return;
- }
-
- cow_file_size += status.cow_file_size();
- total_cow_size += status.cow_file_size() + status.cow_partition_size();
- estimated_cow_size += status.estimated_cow_size();
- }
-
- stats->set_cow_file_size(cow_file_size);
- stats->set_total_cow_size_bytes(total_cow_size);
- stats->set_estimated_cow_size_bytes(estimated_cow_size);
-}
-
-bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
- const std::chrono::milliseconds& timeout_ms) {
- auto& dm = DeviceMapper::Instance();
- auto start = std::chrono::steady_clock::now();
- while (true) {
- if (dm.DeleteDeviceIfExists(name)) {
- return true;
- }
- auto now = std::chrono::steady_clock::now();
- auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
- if (elapsed >= timeout_ms) {
- break;
- }
- std::this_thread::sleep_for(400ms);
- }
-
- // Try to diagnose why this failed. First get the actual device path.
- std::string full_path;
- if (!dm.GetDmDevicePathByName(name, &full_path)) {
- LOG(ERROR) << "Unable to diagnose DM_DEV_REMOVE failure.";
- return false;
- }
-
- // Check for child dm-devices.
- std::string block_name = android::base::Basename(full_path);
- std::string sysfs_holders = "/sys/class/block/" + block_name + "/holders";
-
- std::error_code ec;
- std::filesystem::directory_iterator dir_iter(sysfs_holders, ec);
- if (auto begin = std::filesystem::begin(dir_iter); begin != std::filesystem::end(dir_iter)) {
- LOG(ERROR) << "Child device-mapper device still mapped: " << begin->path();
- return false;
- }
-
- // Check for mounted partitions.
- android::fs_mgr::Fstab fstab;
- android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab);
- for (const auto& entry : fstab) {
- if (android::base::Basename(entry.blk_device) == block_name) {
- LOG(ERROR) << "Partition still mounted: " << entry.mount_point;
- return false;
- }
- }
-
- // Check for detached mounted partitions.
- for (const auto& fs : std::filesystem::directory_iterator("/sys/fs", ec)) {
- std::string fs_type = android::base::Basename(fs.path().c_str());
- if (!(fs_type == "ext4" || fs_type == "f2fs")) {
- continue;
- }
-
- std::string path = fs.path().c_str() + "/"s + block_name;
- if (access(path.c_str(), F_OK) == 0) {
- LOG(ERROR) << "Block device was lazily unmounted and is still in-use: " << full_path
- << "; possibly open file descriptor or attached loop device.";
- return false;
- }
- }
-
- LOG(ERROR) << "Device-mapper device " << name << "(" << full_path << ")"
- << " still in use."
- << " Probably a file descriptor was leaked or held open, or a loop device is"
- << " attached.";
- return false;
-}
-
-MergeFailureCode SnapshotManager::ReadMergeFailureCode() {
- auto lock = LockExclusive();
- if (!lock) return MergeFailureCode::AcquireLock;
-
- SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
- if (status.state() != UpdateState::MergeFailed) {
- return MergeFailureCode::Ok;
- }
- return status.merge_failure_code();
-}
-
-std::string SnapshotManager::ReadSourceBuildFingerprint() {
- auto lock = LockExclusive();
- if (!lock) return {};
-
- SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
- return status.source_build_fingerprint();
-}
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
deleted file mode 100644
index aced3edf5..000000000
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stddef.h>
-#include <stdint.h>
-#include <sysexits.h>
-
-#include <functional>
-#include <sstream>
-#include <tuple>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/result.h>
-#include <gtest/gtest.h>
-#include <src/libfuzzer/libfuzzer_macro.h>
-#include <storage_literals/storage_literals.h>
-
-#include "fuzz_utils.h"
-#include "snapshot_fuzz_utils.h"
-
-using android::base::Error;
-using android::base::GetBoolProperty;
-using android::base::LogId;
-using android::base::LogSeverity;
-using android::base::ReadFileToString;
-using android::base::Result;
-using android::base::SetLogger;
-using android::base::StderrLogger;
-using android::base::StdioLogger;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fuzz::CheckedCast;
-using android::snapshot::SnapshotFuzzData;
-using android::snapshot::SnapshotFuzzEnv;
-using chromeos_update_engine::DeltaArchiveManifest;
-using google::protobuf::FieldDescriptor;
-using google::protobuf::Message;
-using google::protobuf::RepeatedPtrField;
-
-// Avoid linking to libgsi since it needs disk I/O.
-namespace android::gsi {
-bool IsGsiRunning() {
- LOG(FATAL) << "Called IsGsiRunning";
- __builtin_unreachable();
-}
-std::string GetDsuSlot(const std::string& install_dir) {
- LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
- __builtin_unreachable();
-}
-} // namespace android::gsi
-
-namespace android::snapshot {
-
-const SnapshotFuzzData* current_data = nullptr;
-const SnapshotTestModule* current_module = nullptr;
-
-SnapshotFuzzEnv* GetSnapshotFuzzEnv();
-
-FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
-
-using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
-using CreateLogicalAndSnapshotPartitionsArgs =
- SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
-using RecoveryCreateSnapshotDevicesArgs =
- SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
-
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
-
-#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...) \
- FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
- ##__VA_ARGS__)
-
-SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
- return snapshot->FinishedSnapshotWrites(wipe);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
- std::function<bool()> before_cancel;
- if (args.has_before_cancel()) {
- before_cancel = [&]() { return args.fail_before_cancel(); };
- }
- return snapshot->ProcessUpdateState({}, before_cancel);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
- double progress;
- return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
- std::function<void()> callback;
- if (has_callback) {
- callback = []() {};
- }
- return snapshot->HandleImminentDataWipe(callback);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
- std::stringstream ss;
- return snapshot->Dump(ss);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
- return snapshot->CreateUpdateSnapshots(manifest);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
- return snapshot->UnmapUpdateSnapshot(name);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
- const CreateLogicalAndSnapshotPartitionsArgs& args) {
- const std::string* super;
- if (args.use_correct_super()) {
- super = &GetSnapshotFuzzEnv()->super();
- } else {
- super = &args.super();
- }
- return snapshot->CreateLogicalAndSnapshotPartitions(
- *super, std::chrono::milliseconds(args.timeout_millis()));
-}
-
-SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
- const RecoveryCreateSnapshotDevicesArgs& args) {
- std::unique_ptr<AutoDevice> device;
- if (args.has_metadata_device_object()) {
- device = std::make_unique<NoOpAutoDevice>(args.metadata_mounted());
- }
- return snapshot->RecoveryCreateSnapshotDevices(device);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
- const CreateLogicalPartitionParamsProto& params_proto) {
- auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
- CreateLogicalPartitionParams params;
- if (params_proto.use_correct_super()) {
- params.block_device = GetSnapshotFuzzEnv()->super();
- } else {
- params.block_device = params_proto.block_device();
- }
- if (params_proto.has_metadata_slot()) {
- params.metadata_slot = params_proto.metadata_slot();
- }
- params.partition_name = params_proto.partition_name();
- params.force_writable = params_proto.force_writable();
- params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
- params.device_name = params_proto.device_name();
- params.partition_opener = partition_opener.get();
- std::string path;
- return snapshot->MapUpdateSnapshot(params, &path);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
- (void)snapshot;
- CHECK(current_module != nullptr);
- CHECK(current_module->device_info != nullptr);
- current_module->device_info->SwitchSlot();
-}
-
-// During global init, log all messages to stdio. This is only done once.
-int AllowLoggingDuringGlobalInit() {
- SetLogger(&StdioLogger);
- return 0;
-}
-
-// Only log fatal messages during tests.
-void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
- unsigned int line, const char* message) {
- if (severity == LogSeverity::FATAL) {
- StderrLogger(logid, severity, tag, file, line, message);
-
- // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
- // nothing else we can do.
- StderrLogger(logid, severity, tag, __FILE__, __LINE__,
- "Attempting to dump current corpus:");
- if (current_data == nullptr) {
- StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
- } else {
- std::string content;
- if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
- StderrLogger(logid, severity, tag, __FILE__, __LINE__,
- "Failed to print corpus to string.");
- } else {
- StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
- }
- }
- }
-}
-// Stop logging (except fatal messages) after global initialization. This is only done once.
-int StopLoggingAfterGlobalInit() {
- (void)GetSnapshotFuzzEnv();
- [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
- SetLogger(&FatalOnlyLogger);
- return 0;
-}
-
-SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
- [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
- static SnapshotFuzzEnv env;
- return &env;
-}
-
-SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
- current_data = &snapshot_fuzz_data;
-
- auto env = GetSnapshotFuzzEnv();
- env->CheckSoftReset();
-
- auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
- current_module = &test_module;
- CHECK(test_module.snapshot);
- return test_module;
-}
-
-void TearDownTest() {
- current_module = nullptr;
- current_data = nullptr;
-}
-
-} // namespace android::snapshot
-
-DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
- using namespace android::snapshot;
-
- [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
- auto test_module = SetUpTest(snapshot_fuzz_data);
- SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
- TearDownTest();
-}
-
-namespace android::snapshot {
-
-// Work-around to cast a 'void' value to Result<void>.
-template <typename T>
-struct GoodResult {
- template <typename F>
- static Result<T> Cast(F&& f) {
- return f();
- }
-};
-
-template <>
-struct GoodResult<void> {
- template <typename F>
- static Result<void> Cast(F&& f) {
- f();
- return {};
- }
-};
-
-class LibsnapshotFuzzerTest : public ::testing::Test {
- protected:
- static void SetUpTestCase() {
- // Do initialization once.
- (void)GetSnapshotFuzzEnv();
- }
- void SetUp() override {
- bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
- if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
- }
- void SetUpFuzzData(const std::string& fn) {
- auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
- std::string proto_text;
- ASSERT_TRUE(ReadFileToString(path, &proto_text));
- snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
- ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
- snapshot_fuzz_data_.get()));
- test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
- }
- void TearDown() override { android::snapshot::TearDownTest(); }
- template <typename FuzzFunction>
- Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
- if (action_index >= snapshot_fuzz_data_->actions_size()) {
- return Error() << "Index " << action_index << " is out of bounds ("
- << snapshot_fuzz_data_->actions_size() << " actions in corpus";
- }
- const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
- const auto* field_desc =
- android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
- action_proto);
- if (field_desc == nullptr) {
- return Error() << "Action at index " << action_index << " has no value defined.";
- }
- if (FuzzFunction::tag != field_desc->number()) {
- return Error() << "Action at index " << action_index << " is expected to be "
- << FuzzFunction::name << ", but it is " << field_desc->name()
- << " in corpus.";
- }
- return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
- return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
- action_proto, field_desc);
- });
- }
-
- std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
- SnapshotTestModule test_module_;
-};
-
-#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
-
-MATCHER_P(ResultIs, expected, "") {
- if (!arg.ok()) {
- *result_listener << arg.error();
- return false;
- }
- *result_listener << "expected: " << expected;
- return arg.value() == expected;
-}
-
-#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
-
-// Check that launch_device.txt is executed correctly.
-TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
- SetUpFuzzData("launch_device.txt");
-
- int i = 0;
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
- ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
- ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
- ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
- ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
-}
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
deleted file mode 100644
index 0096f85f8..000000000
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ /dev/null
@@ -1,513 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <ftw.h>
-#include <inttypes.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sysexits.h>
-
-#include <chrono>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <fs_mgr.h>
-#include <libsnapshot/auto_device.h>
-#include <libsnapshot/snapshot.h>
-#include <storage_literals/storage_literals.h>
-
-#include "snapshot_fuzz_utils.h"
-#include "utility.h"
-
-// Prepends the errno string, but it is good enough.
-#ifndef PCHECK
-#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
-#endif
-
-using namespace android::storage_literals;
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-
-using android::base::Basename;
-using android::base::ReadFileToString;
-using android::base::SetProperty;
-using android::base::Split;
-using android::base::StartsWith;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteStringToFile;
-using android::dm::DeviceMapper;
-using android::dm::DmTarget;
-using android::dm::LoopControl;
-using android::fiemap::IImageManager;
-using android::fiemap::ImageManager;
-using android::fs_mgr::BlockDeviceInfo;
-using android::fs_mgr::FstabEntry;
-using android::fs_mgr::IPartitionOpener;
-using chromeos_update_engine::DynamicPartitionMetadata;
-
-static const char MNT_DIR[] = "/mnt";
-static const char BLOCK_SYSFS[] = "/sys/block";
-
-static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
-static const auto SUPER_IMAGE_SIZE = 16_MiB;
-static const auto DATA_IMAGE_SIZE = 16_MiB;
-static const auto FAKE_ROOT_SIZE = 64_MiB;
-
-namespace android::snapshot {
-
-bool Mkdir(const std::string& path) {
- if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
- PLOG(ERROR) << "Cannot create " << path;
- return false;
- }
- return true;
-}
-
-bool RmdirRecursive(const std::string& path) {
- auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
- switch (file_type) {
- case FTW_D:
- case FTW_DP:
- case FTW_DNR:
- if (rmdir(child) == -1) {
- PLOG(ERROR) << "rmdir " << child;
- return -1;
- }
- return 0;
- case FTW_NS:
- default:
- if (rmdir(child) != -1) break;
- [[fallthrough]];
- case FTW_F:
- case FTW_SL:
- case FTW_SLN:
- if (unlink(child) == -1) {
- PLOG(ERROR) << "unlink " << child;
- return -1;
- }
- return 0;
- }
- return 0;
- };
-
- return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
-}
-
-std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) {
- if (target.spec.target_type != "linear"s) return {};
- auto tokens = Split(target.data, " ");
- CHECK_EQ(2, tokens.size());
- return tokens[0];
-}
-
-std::vector<std::string> GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) {
- if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s)
- return {};
- auto tokens = Split(target.data, " ");
- CHECK_EQ(4, tokens.size());
- return {tokens[0], tokens[1]};
-}
-
-bool ShouldDeleteLoopDevice(const std::string& node) {
- std::string backing_file;
- if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) {
- if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) {
- return true;
- }
- }
- return false;
-}
-
-std::vector<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
- auto& dm = DeviceMapper::Instance();
- std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableInfo(dev_name, &table)) {
- PCHECK(errno == ENODEV);
- return {};
- }
- return table;
-}
-
-std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
- std::set<std::string> ret;
- for (const auto& child_target : GetTableInfoIfExists(child_dev)) {
- auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target);
- ret.insert(snapshot_bases.begin(), snapshot_bases.end());
-
- auto linear_base = GetLinearBaseDeviceString(child_target);
- if (!linear_base.empty()) {
- ret.insert(linear_base);
- }
- }
- return ret;
-}
-
-using PropertyList = std::set<std::string>;
-void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
- reinterpret_cast<PropertyList*>(cookie)->insert(key);
-}
-
-// Attempt to delete all devices that is based on dev_name, including itself.
-void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
- uint64_t depth = 100) {
- CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name
- << ". There may be devices referencing itself. Check `dmctl list devices -v`.";
-
- auto& dm = DeviceMapper::Instance();
- auto table = GetTableInfoIfExists(dev_name);
- if (table.empty()) {
- PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
- return;
- }
-
- if (!known_allow_delete) {
- for (const auto& target : table) {
- auto base_device_string = GetLinearBaseDeviceString(target);
- if (base_device_string.empty()) continue;
- if (ShouldDeleteLoopDevice(
- StringPrintf("/sys/dev/block/%s", base_device_string.data()))) {
- known_allow_delete = true;
- break;
- }
- }
- }
- if (!known_allow_delete) {
- return;
- }
-
- std::string dev_string;
- PCHECK(dm.GetDeviceString(dev_name, &dev_string));
-
- std::vector<DeviceMapper::DmBlockDevice> devices;
- PCHECK(dm.GetAvailableDevices(&devices));
- for (const auto& child_dev : devices) {
- auto child_bases = GetAllBaseDeviceStrings(child_dev.name());
- if (child_bases.find(dev_string) != child_bases.end()) {
- CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1);
- }
- }
-
- PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
-}
-
-// Attempt to clean up residues from previous runs.
-void CheckCleanupDeviceMapperDevices() {
- auto& dm = DeviceMapper::Instance();
- std::vector<DeviceMapper::DmBlockDevice> devices;
- PCHECK(dm.GetAvailableDevices(&devices));
-
- for (const auto& dev : devices) {
- CheckDeleteDeviceMapperTree(dev.name());
- }
-}
-
-void CheckUmount(const std::string& path) {
- PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL)
- << path;
-}
-
-void CheckDetachLoopDevices(const std::set<std::string>& exclude_names = {}) {
- // ~SnapshotFuzzEnv automatically does the following.
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(BLOCK_SYSFS), closedir);
- PCHECK(dir != nullptr) << BLOCK_SYSFS;
- LoopControl loop_control;
- dirent* dp;
- while ((dp = readdir(dir.get())) != nullptr) {
- if (exclude_names.find(dp->d_name) != exclude_names.end()) {
- continue;
- }
- if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) {
- continue;
- }
- PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data()));
- }
-}
-
-void CheckUmountAll() {
- CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data");
- CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME);
-}
-
-class AutoDeleteDir : public AutoDevice {
- public:
- static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
- if (!Mkdir(path)) {
- return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
- }
- return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
- }
- ~AutoDeleteDir() {
- if (!HasDevice()) return;
- PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_;
- }
-
- private:
- AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
-};
-
-class AutoUnmount : public AutoDevice {
- public:
- ~AutoUnmount() {
- if (!HasDevice()) return;
- CheckUmount(name_);
- }
- AutoUnmount(const std::string& path) : AutoDevice(path) {}
-};
-
-class AutoUnmountTmpfs : public AutoUnmount {
- public:
- static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
- if (mount("tmpfs", path.c_str(), "tmpfs", 0,
- (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
- PLOG(ERROR) << "Cannot mount " << path;
- return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
- }
- return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
- }
- private:
- using AutoUnmount::AutoUnmount;
-};
-
-// A directory on tmpfs. Upon destruct, it is unmounted and deleted.
-class AutoMemBasedDir : public AutoDevice {
- public:
- static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
- auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
- ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
- if (!ret->auto_delete_mount_dir_->HasDevice()) {
- return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
- }
- ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size);
- if (!ret->auto_umount_mount_point_->HasDevice()) {
- return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
- }
- // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
- // not wrapped with AutoDeleteDir.
- if (!Mkdir(ret->tmp_path())) {
- return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
- }
- if (!Mkdir(ret->persist_path())) {
- return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
- }
- return ret;
- }
- // Return the temporary scratch directory.
- std::string tmp_path() const {
- CHECK(HasDevice());
- return mount_path() + "/tmp";
- }
- // Return the temporary scratch directory.
- std::string persist_path() const {
- CHECK(HasDevice());
- return mount_path() + "/persist";
- }
- // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
- void CheckSoftReset() {
- PCHECK(RmdirRecursive(tmp_path()));
- PCHECK(Mkdir(tmp_path()));
- }
-
- private:
- AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
- std::string mount_path() const {
- CHECK(HasDevice());
- return MNT_DIR + "/"s + name_;
- }
- std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
- std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
-};
-
-SnapshotFuzzEnv::SnapshotFuzzEnv() {
- CheckCleanupDeviceMapperDevices();
- CheckDetachLoopDevices();
- CheckUmountAll();
-
- fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
- CHECK(fake_root_ != nullptr);
- CHECK(fake_root_->HasDevice());
- loop_control_ = std::make_unique<LoopControl>();
-
- fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s;
- auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_);
- CHECK(auto_delete_data_mount_point_ != nullptr);
- CHECK(auto_delete_data_mount_point_->HasDevice());
-
- const auto& fake_persist_path = fake_root_->persist_path();
- mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE,
- loop_control_.get(), &fake_super_);
- mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE,
- loop_control_.get(), &fake_data_block_device_);
- mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_);
-}
-
-SnapshotFuzzEnv::~SnapshotFuzzEnv() {
- CheckCleanupDeviceMapperDevices();
- mounted_data_ = nullptr;
- auto_delete_data_mount_point_ = nullptr;
- mapped_data_ = nullptr;
- mapped_super_ = nullptr;
- CheckDetachLoopDevices();
- loop_control_ = nullptr;
- fake_root_ = nullptr;
- CheckUmountAll();
-}
-
-void CheckZeroFill(const std::string& file, size_t size) {
- std::string zeros(size, '\0');
- PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
-}
-
-void SnapshotFuzzEnv::CheckSoftReset() {
- fake_root_->CheckSoftReset();
- CheckZeroFill(super(), SUPER_IMAGE_SIZE);
- CheckCleanupDeviceMapperDevices();
- CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
-}
-
-std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager() {
- auto metadata_dir = fake_root_->tmp_path() + "/images_manager_metadata";
- auto data_dir = fake_data_mount_point_ + "/image_manager_data";
- PCHECK(Mkdir(metadata_dir));
- PCHECK(Mkdir(data_dir));
- return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
-}
-
-// Helper to create a loop device for a file.
-static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
- const std::chrono::milliseconds& timeout_ms, std::string* path) {
- static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
- android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
- PCHECK(file_fd >= 0) << "Could not open file: " << file;
- CHECK(control->Attach(file_fd, timeout_ms, path))
- << "Could not create loop device for: " << file;
-}
-
-class AutoDetachLoopDevice : public AutoDevice {
- public:
- AutoDetachLoopDevice(LoopControl* control, const std::string& device)
- : AutoDevice(device), control_(control) {}
- ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; }
-
- private:
- LoopControl* control_;
-};
-
-std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapImage(const std::string& img_path,
- uint64_t size, LoopControl* control,
- std::string* mapped_path) {
- CheckZeroFill(img_path, size);
- CheckCreateLoopDevice(control, img_path, 1s, mapped_path);
-
- return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
-}
-
-SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
- SnapshotTestModule ret;
- auto partition_opener = std::make_unique<TestPartitionOpener>(super());
- ret.opener = partition_opener.get();
- CheckWriteSuperMetadata(data, *partition_opener);
- auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
- PCHECK(Mkdir(metadata_dir));
- if (data.has_metadata_snapshots_dir()) {
- PCHECK(Mkdir(metadata_dir + "/snapshots"));
- }
-
- ret.device_info = new SnapshotFuzzDeviceInfo(this, data.device_info_data(),
- std::move(partition_opener), metadata_dir);
- auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
- ret.snapshot = std::move(snapshot);
-
- return ret;
-}
-
-const std::string& SnapshotFuzzEnv::super() const {
- return fake_super_;
-}
-
-void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
- const IPartitionOpener& opener) {
- if (!data.is_super_metadata_valid()) {
- // Leave it zero.
- return;
- }
-
- BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
- std::vector<BlockDeviceInfo> devices = {super_device};
- auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
- CHECK(builder != nullptr);
-
- // Attempt to create a super partition metadata using proto. All errors are ignored.
- for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
- (void)builder->AddGroup(group_proto.name(), group_proto.size());
- for (const auto& partition_name : group_proto.partition_names()) {
- (void)builder->AddPartition(partition_name, group_proto.name(),
- LP_PARTITION_ATTR_READONLY);
- }
- }
-
- for (const auto& partition_proto : data.super_data().partitions()) {
- auto p = builder->FindPartition(partition_proto.partition_name());
- if (p == nullptr) continue;
- (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
- }
-
- auto metadata = builder->Export();
- // metadata may be nullptr if it is not valid (e.g. partition name too long).
- // In this case, just use empty super partition data.
- if (metadata == nullptr) {
- builder = MetadataBuilder::New(devices, "super", 65536, 2);
- CHECK(builder != nullptr);
- metadata = builder->Export();
- CHECK(metadata != nullptr);
- }
- CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
-}
-
-std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
- const std::string& mount_point) {
- FstabEntry entry{
- .blk_device = blk_device,
- .length = static_cast<off64_t>(DATA_IMAGE_SIZE),
- .fs_type = "ext4",
- .mount_point = mount_point,
- };
- CHECK(0 == fs_mgr_do_format(entry, false /* crypt_footer */));
- CHECK(0 == fs_mgr_do_mount_one(entry));
- return std::make_unique<AutoUnmount>(mount_point);
-}
-
-SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
- // Remove relevant gsid.mapped_images.* props.
- for (const auto& name : mapped_) {
- CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
- }
-}
-
-bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
- const std::chrono::milliseconds& timeout_ms,
- std::string* path) {
- if (impl_->MapImageDevice(name, timeout_ms, path)) {
- mapped_.insert(name);
- return true;
- }
- return false;
-}
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
deleted file mode 100644
index 3ed27c836..000000000
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <memory>
-#include <set>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android/snapshot/snapshot_fuzz.pb.h>
-#include <libdm/loop_control.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/liblp.h>
-#include <libsnapshot/auto_device.h>
-#include <libsnapshot/test_helpers.h>
-
-// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
-// by SnapshotManager.
-
-#include "android/snapshot/snapshot_fuzz.pb.h"
-
-namespace android::snapshot {
-
-class AutoMemBasedDir;
-class SnapshotFuzzDeviceInfo;
-
-class NoOpAutoDevice : public AutoDevice {
- public:
- NoOpAutoDevice(bool mounted) : AutoDevice(mounted ? "no_op" : "") {}
-};
-
-struct SnapshotTestModule {
- std::unique_ptr<ISnapshotManager> snapshot;
- SnapshotFuzzDeviceInfo* device_info = nullptr;
- TestPartitionOpener* opener = nullptr;
-};
-
-// Prepare test environment. This has a heavy overhead and should be done once.
-class SnapshotFuzzEnv {
- public:
- // Check if test should run at all.
- static bool ShouldSkipTest();
-
- // Initialize the environment.
- SnapshotFuzzEnv();
- ~SnapshotFuzzEnv();
-
- // Soft reset part of the environment before running the next test.
- // Abort if fails.
- void CheckSoftReset();
-
- // Create a snapshot manager for this test run.
- // Client is responsible for maintaining the lifetime of |data| over the life time of
- // ISnapshotManager.
- SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data);
-
- std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager();
-
- // Return path to super partition.
- const std::string& super() const;
-
- private:
- std::unique_ptr<AutoMemBasedDir> fake_root_;
- std::unique_ptr<android::dm::LoopControl> loop_control_;
- std::string fake_data_mount_point_;
- std::unique_ptr<AutoDevice> auto_delete_data_mount_point_;
- std::unique_ptr<AutoDevice> mapped_super_;
- std::string fake_super_;
- std::unique_ptr<AutoDevice> mapped_data_;
- std::string fake_data_block_device_;
- std::unique_ptr<AutoDevice> mounted_data_;
-
- static std::unique_ptr<AutoDevice> CheckMapImage(const std::string& fake_persist_path,
- uint64_t size,
- android::dm::LoopControl* control,
- std::string* mapped_path);
- static std::unique_ptr<AutoDevice> CheckMountFormatData(const std::string& blk_device,
- const std::string& mount_point);
-
- void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
- const android::fs_mgr::IPartitionOpener& opener);
-};
-
-class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
- public:
- // Client is responsible for maintaining the lifetime of |data|.
- SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data,
- std::unique_ptr<TestPartitionOpener>&& partition_opener,
- const std::string& metadata_dir)
- : env_(env),
- data_(&data),
- partition_opener_(std::move(partition_opener)),
- metadata_dir_(metadata_dir) {}
-
- // Following APIs are mocked.
- std::string GetMetadataDir() const override { return metadata_dir_; }
- std::string GetSuperDevice(uint32_t) const override {
- // TestPartitionOpener can recognize this.
- return "super";
- }
- const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
- return *partition_opener_;
- }
-
- // Following APIs are fuzzed.
- std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; }
- std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; }
- bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
- bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
- return data_->allow_set_boot_control_merge_status();
- }
- bool SetSlotAsUnbootable(unsigned int) override {
- return data_->allow_set_slot_as_unbootable();
- }
- bool IsRecovery() const override { return data_->is_recovery(); }
- bool IsFirstStageInit() const override { return false; }
- std::unique_ptr<IImageManager> OpenImageManager() const {
- return env_->CheckCreateFakeImageManager();
- }
-
- void SwitchSlot() { switched_slot_ = !switched_slot_; }
-
- private:
- SnapshotFuzzEnv* env_;
- const FuzzDeviceInfoData* data_;
- std::unique_ptr<TestPartitionOpener> partition_opener_;
- std::string metadata_dir_;
- bool switched_slot_ = false;
-
- bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
-};
-
-// A spy class on ImageManager implementation. Upon destruction, unmaps all images
-// map through this object.
-class SnapshotFuzzImageManager : public android::fiemap::IImageManager {
- public:
- static std::unique_ptr<SnapshotFuzzImageManager> Open(const std::string& metadata_dir,
- const std::string& data_dir) {
- auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir);
- if (impl == nullptr) return nullptr;
- return std::unique_ptr<SnapshotFuzzImageManager>(
- new SnapshotFuzzImageManager(std::move(impl)));
- }
-
- ~SnapshotFuzzImageManager();
-
- // Spied APIs.
- bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
- std::string* path) override;
-
- // Other functions call through.
- android::fiemap::FiemapStatus CreateBackingImage(
- const std::string& name, uint64_t size, int flags,
- std::function<bool(uint64_t, uint64_t)>&& on_progress) override {
- return impl_->CreateBackingImage(name, size, flags, std::move(on_progress));
- }
- bool DeleteBackingImage(const std::string& name) override {
- return impl_->DeleteBackingImage(name);
- }
- bool UnmapImageDevice(const std::string& name) override {
- return impl_->UnmapImageDevice(name);
- }
- bool BackingImageExists(const std::string& name) override {
- return impl_->BackingImageExists(name);
- }
- bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); }
- bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
- std::string* dev) override {
- return impl_->MapImageWithDeviceMapper(opener, name, dev);
- }
- bool GetMappedImageDevice(const std::string& name, std::string* device) override {
- return impl_->GetMappedImageDevice(name, device);
- }
- bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override {
- return impl_->MapAllImages(init);
- }
- bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); }
- bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); }
- std::vector<std::string> GetAllBackingImages() override { return impl_->GetAllBackingImages(); }
- android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name,
- uint64_t bytes) override {
- return impl_->ZeroFillNewImage(name, bytes);
- }
- bool RemoveAllImages() override { return impl_->RemoveAllImages(); }
- bool UnmapImageIfExists(const std::string& name) override {
- return impl_->UnmapImageIfExists(name);
- }
-
- private:
- std::unique_ptr<android::fiemap::IImageManager> impl_;
- std::set<std::string> mapped_;
-
- SnapshotFuzzImageManager(std::unique_ptr<android::fiemap::IImageManager>&& impl)
- : impl_(std::move(impl)) {}
-};
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
index 17a0c96a8..60bf79647 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
@@ -39,8 +39,6 @@ namespace snapshot {
SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint32_t target_slot,
const DeltaArchiveManifest& manifest)
: builder_(builder), target_suffix_(SlotSuffixForSlotNumber(target_slot)) {
- partial_update_ = manifest.partial_update();
-
if (!manifest.has_dynamic_partition_metadata()) {
return;
}
@@ -64,7 +62,6 @@ SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint3
std::string(it->second) + target_suffix_, &p});
}
}
-
}
bool SnapshotMetadataUpdater::ShrinkPartitions() const {
@@ -85,18 +82,6 @@ bool SnapshotMetadataUpdater::ShrinkPartitions() const {
}
bool SnapshotMetadataUpdater::DeletePartitions() const {
- // For partial update, not all dynamic partitions are included in the payload.
- // TODO(xunchang) delete the untouched partitions whose group is in the payload.
- // e.g. Delete vendor in the following scenario
- // On device:
- // Group A: system, vendor
- // In payload:
- // Group A: system
- if (partial_update_) {
- LOG(INFO) << "Skip deleting partitions for partial update";
- return true;
- }
-
std::vector<std::string> partitions_to_delete;
// Don't delete partitions in groups where the group name doesn't have target_suffix,
// e.g. default.
@@ -154,11 +139,6 @@ bool SnapshotMetadataUpdater::ShrinkGroups() const {
}
bool SnapshotMetadataUpdater::DeleteGroups() const {
- if (partial_update_) {
- LOG(INFO) << "Skip deleting groups for partial update";
- return true;
- }
-
std::vector<std::string> existing_groups = builder_->ListGroups();
for (const auto& existing_group_name : existing_groups) {
// Don't delete groups without target suffix, e.g. default.
@@ -174,9 +154,9 @@ bool SnapshotMetadataUpdater::DeleteGroups() const {
if (iter != groups_.end()) {
continue;
}
- // Update package metadata doesn't have this group. Before deleting it, check that it
- // doesn't have any partitions left. Update metadata shouldn't assign any partitions to
- // this group, so all partitions that originally belong to this group should be moved by
+ // Update package metadata doesn't have this group. Before deleting it, sanity check that it
+ // doesn't have any partitions left. Update metadata shouldn't assign any partitions to this
+ // group, so all partitions that originally belong to this group should be moved by
// MovePartitionsToDefault at this point.
auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);
if (!existing_partitions_in_group.empty()) {
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.h b/fs_mgr/libsnapshot/snapshot_metadata_updater.h
index 5b1cbf91a..83c94607d 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.h
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.h
@@ -79,7 +79,6 @@ class SnapshotMetadataUpdater {
const std::string target_suffix_;
std::vector<Group> groups_;
std::vector<Partition> partitions_;
- bool partial_update_{false};
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
deleted file mode 100644
index 5ee8e25d1..000000000
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ /dev/null
@@ -1,332 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "snapshot_reader.h"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <ext4_utils/ext4_utils.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-
-// Not supported.
-bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
- errno = EINVAL;
- return false;
-}
-
-bool ReadOnlyFileDescriptor::Open(const char*, int) {
- errno = EINVAL;
- return false;
-}
-
-ssize_t ReadOnlyFileDescriptor::Write(const void*, size_t) {
- errno = EINVAL;
- return false;
-}
-
-bool ReadOnlyFileDescriptor::BlkIoctl(int, uint64_t, uint64_t, int*) {
- errno = EINVAL;
- return false;
-}
-
-ReadFdFileDescriptor::ReadFdFileDescriptor(android::base::unique_fd&& fd) : fd_(std::move(fd)) {}
-
-ssize_t ReadFdFileDescriptor::Read(void* buf, size_t count) {
- return read(fd_.get(), buf, count);
-}
-
-off64_t ReadFdFileDescriptor::Seek(off64_t offset, int whence) {
- return lseek(fd_.get(), offset, whence);
-}
-
-uint64_t ReadFdFileDescriptor::BlockDevSize() {
- return get_block_device_size(fd_.get());
-}
-
-bool ReadFdFileDescriptor::Close() {
- fd_ = {};
- return true;
-}
-
-bool ReadFdFileDescriptor::IsSettingErrno() {
- return true;
-}
-
-bool ReadFdFileDescriptor::IsOpen() {
- return fd_ >= 0;
-}
-
-bool ReadFdFileDescriptor::Flush() {
- return true;
-}
-
-bool CompressedSnapshotReader::SetCow(std::unique_ptr<CowReader>&& cow) {
- cow_ = std::move(cow);
-
- CowHeader header;
- if (!cow_->GetHeader(&header)) {
- return false;
- }
- block_size_ = header.block_size;
-
- // Populate the operation map.
- op_iter_ = cow_->GetOpIter();
- while (!op_iter_->Done()) {
- const CowOperation* op = &op_iter_->Get();
- if (IsMetadataOp(*op)) {
- op_iter_->Next();
- continue;
- }
- if (op->new_block >= ops_.size()) {
- ops_.resize(op->new_block + 1, nullptr);
- }
- ops_[op->new_block] = op;
- op_iter_->Next();
- }
-
- return true;
-}
-
-void CompressedSnapshotReader::SetSourceDevice(const std::string& source_device) {
- source_device_ = {source_device};
-}
-
-void CompressedSnapshotReader::SetBlockDeviceSize(uint64_t block_device_size) {
- block_device_size_ = block_device_size;
-}
-
-borrowed_fd CompressedSnapshotReader::GetSourceFd() {
- if (source_fd_ < 0) {
- if (!source_device_) {
- LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
- errno = EINVAL;
- return {-1};
- }
- source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
- if (source_fd_ < 0) {
- PLOG(ERROR) << "open " << *source_device_;
- return {-1};
- }
- }
- return source_fd_;
-}
-
-class MemoryByteSink : public IByteSink {
- public:
- MemoryByteSink(void* buf, size_t count) {
- buf_ = reinterpret_cast<uint8_t*>(buf);
- pos_ = buf_;
- end_ = buf_ + count;
- }
-
- void* GetBuffer(size_t requested, size_t* actual) override {
- *actual = std::min(remaining(), requested);
- if (!*actual) {
- return nullptr;
- }
-
- uint8_t* start = pos_;
- pos_ += *actual;
- return start;
- }
-
- bool ReturnData(void*, size_t) override { return true; }
-
- uint8_t* buf() const { return buf_; }
- uint8_t* pos() const { return pos_; }
- size_t remaining() const { return end_ - pos_; }
-
- private:
- uint8_t* buf_;
- uint8_t* pos_;
- uint8_t* end_;
-};
-
-ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
- // Find the start and end chunks, inclusive.
- uint64_t start_chunk = offset_ / block_size_;
- uint64_t end_chunk = (offset_ + count - 1) / block_size_;
-
- // Chop off the first N bytes if the position is not block-aligned.
- size_t start_offset = offset_ % block_size_;
-
- MemoryByteSink sink(buf, count);
-
- size_t initial_bytes = std::min(block_size_ - start_offset, sink.remaining());
- ssize_t rv = ReadBlock(start_chunk, &sink, start_offset, initial_bytes);
- if (rv < 0) {
- return -1;
- }
- offset_ += rv;
-
- for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
- ssize_t rv = ReadBlock(chunk, &sink, 0);
- if (rv < 0) {
- return -1;
- }
- offset_ += rv;
- }
-
- if (sink.remaining()) {
- ssize_t rv = ReadBlock(end_chunk, &sink, 0, {sink.remaining()});
- if (rv < 0) {
- return -1;
- }
- offset_ += rv;
- }
-
- errno = 0;
-
- DCHECK(sink.pos() - sink.buf() == count);
- return count;
-}
-
-// Discard the first N bytes of a sink request, or any excess bytes.
-class PartialSink : public MemoryByteSink {
- public:
- PartialSink(void* buffer, size_t size, size_t ignore_start)
- : MemoryByteSink(buffer, size), ignore_start_(ignore_start) {}
-
- void* GetBuffer(size_t requested, size_t* actual) override {
- // Throw away the first N bytes if needed.
- if (ignore_start_) {
- *actual = std::min({requested, ignore_start_, sizeof(discard_)});
- ignore_start_ -= *actual;
- return discard_;
- }
- // Throw away any excess bytes if needed.
- if (remaining() == 0) {
- *actual = std::min(requested, sizeof(discard_));
- return discard_;
- }
- return MemoryByteSink::GetBuffer(requested, actual);
- }
-
- private:
- size_t ignore_start_;
- char discard_[4096];
-};
-
-ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
- const std::optional<uint64_t>& max_bytes) {
- size_t bytes_to_read = block_size_;
- if (max_bytes) {
- bytes_to_read = *max_bytes;
- }
-
- // The offset is relative to the chunk; we should be reading no more than
- // one chunk.
- CHECK(start_offset + bytes_to_read <= block_size_);
-
- const CowOperation* op = nullptr;
- if (chunk < ops_.size()) {
- op = ops_[chunk];
- }
-
- size_t actual;
- void* buffer = sink->GetBuffer(bytes_to_read, &actual);
- if (!buffer || actual < bytes_to_read) {
- // This should never happen unless we calculated the read size wrong
- // somewhere. MemoryByteSink always fulfills the entire requested
- // region unless there's not enough buffer remaining.
- LOG(ERROR) << "Asked for buffer of size " << bytes_to_read << ", got " << actual;
- errno = EINVAL;
- return -1;
- }
-
- if (!op || op->type == kCowCopyOp) {
- borrowed_fd fd = GetSourceFd();
- if (fd < 0) {
- // GetSourceFd sets errno.
- return -1;
- }
-
- if (op) {
- chunk = op->source;
- }
-
- off64_t offset = (chunk * block_size_) + start_offset;
- if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
- PLOG(ERROR) << "read " << *source_device_;
- // ReadFullyAtOffset sets errno.
- return -1;
- }
- } else if (op->type == kCowZeroOp) {
- memset(buffer, 0, bytes_to_read);
- } else if (op->type == kCowReplaceOp) {
- PartialSink partial_sink(buffer, bytes_to_read, start_offset);
- if (!cow_->ReadData(*op, &partial_sink)) {
- LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
- errno = EIO;
- return -1;
- }
- } else {
- LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
- errno = EINVAL;
- return -1;
- }
-
- // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
- return bytes_to_read;
-}
-
-off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
- switch (whence) {
- case SEEK_SET:
- offset_ = offset;
- break;
- case SEEK_END:
- offset_ = static_cast<off64_t>(block_device_size_) + offset;
- break;
- case SEEK_CUR:
- offset_ += offset;
- break;
- default:
- LOG(ERROR) << "Unrecognized seek whence: " << whence;
- errno = EINVAL;
- return -1;
- }
- return offset_;
-}
-
-uint64_t CompressedSnapshotReader::BlockDevSize() {
- return block_device_size_;
-}
-
-bool CompressedSnapshotReader::Close() {
- cow_ = nullptr;
- source_fd_ = {};
- return true;
-}
-
-bool CompressedSnapshotReader::IsSettingErrno() {
- return true;
-}
-
-bool CompressedSnapshotReader::IsOpen() {
- return cow_ != nullptr;
-}
-
-bool CompressedSnapshotReader::Flush() {
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/snapshot_reader.h
deleted file mode 100644
index a3e7e22a0..000000000
--- a/fs_mgr/libsnapshot/snapshot_reader.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#pragma once
-
-#include <optional>
-#include <vector>
-
-#include <android-base/file.h>
-#include <libsnapshot/cow_reader.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android {
-namespace snapshot {
-
-class ReadOnlyFileDescriptor : public chromeos_update_engine::FileDescriptor {
- public:
- bool Open(const char* path, int flags, mode_t mode) override;
- bool Open(const char* path, int flags) override;
- ssize_t Write(const void* buf, size_t count) override;
- bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;
-};
-
-class ReadFdFileDescriptor : public ReadOnlyFileDescriptor {
- public:
- explicit ReadFdFileDescriptor(android::base::unique_fd&& fd);
-
- ssize_t Read(void* buf, size_t count) override;
- off64_t Seek(off64_t offset, int whence) override;
- uint64_t BlockDevSize() override;
- bool Close() override;
- bool IsSettingErrno() override;
- bool IsOpen() override;
- bool Flush() override;
-
- private:
- android::base::unique_fd fd_;
-};
-
-class CompressedSnapshotReader : public ReadOnlyFileDescriptor {
- public:
- bool SetCow(std::unique_ptr<CowReader>&& cow);
- void SetSourceDevice(const std::string& source_device);
- void SetBlockDeviceSize(uint64_t block_device_size);
-
- ssize_t Read(void* buf, size_t count) override;
- off64_t Seek(off64_t offset, int whence) override;
- uint64_t BlockDevSize() override;
- bool Close() override;
- bool IsSettingErrno() override;
- bool IsOpen() override;
- bool Flush() override;
-
- private:
- ssize_t ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
- const std::optional<uint64_t>& max_bytes = {});
- android::base::borrowed_fd GetSourceFd();
-
- std::unique_ptr<CowReader> cow_;
- std::unique_ptr<ICowOpIter> op_iter_;
- uint32_t block_size_ = 0;
-
- std::optional<std::string> source_device_;
- android::base::unique_fd source_fd_;
- uint64_t block_device_size_ = 0;
- off64_t offset_ = 0;
-
- std::vector<const CowOperation*> ops_;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
deleted file mode 100644
index 937305951..000000000
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <libsnapshot/snapshot.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/cow_writer.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using chromeos_update_engine::FileDescriptor;
-
-static constexpr uint32_t kBlockSize = 4096;
-static constexpr size_t kBlockCount = 10;
-
-class OfflineSnapshotTest : public ::testing::Test {
- protected:
- virtual void SetUp() override {
- base_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(base_->fd, 0) << strerror(errno);
-
- cow_ = std::make_unique<TemporaryFile>();
- ASSERT_GE(cow_->fd, 0) << strerror(errno);
-
- WriteBaseDevice();
- }
-
- virtual void TearDown() override {
- base_ = nullptr;
- cow_ = nullptr;
- base_blocks_ = {};
- }
-
- void WriteBaseDevice() {
- unique_fd random(open("/dev/urandom", O_RDONLY));
- ASSERT_GE(random, 0);
-
- for (size_t i = 0; i < kBlockCount; i++) {
- std::string block(kBlockSize, 0);
- ASSERT_TRUE(android::base::ReadFully(random, block.data(), block.size()));
- ASSERT_TRUE(android::base::WriteFully(base_->fd, block.data(), block.size()));
- base_blocks_.emplace_back(std::move(block));
- }
- ASSERT_EQ(fsync(base_->fd), 0);
- }
-
- void WriteCow(ISnapshotWriter* writer) {
- std::string new_block = MakeNewBlockString();
-
- ASSERT_TRUE(writer->AddCopy(3, 0));
- ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
- ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
- ASSERT_TRUE(writer->Finalize());
- }
-
- void TestBlockReads(ISnapshotWriter* writer) {
- auto reader = writer->OpenReader();
- ASSERT_NE(reader, nullptr);
-
- // Test that unchanged blocks are not modified.
- std::unordered_set<size_t> changed_blocks = {3, 5, 7, 8};
- for (size_t i = 0; i < kBlockCount; i++) {
- if (changed_blocks.count(i)) {
- continue;
- }
-
- std::string block(kBlockSize, 0);
- ASSERT_EQ(reader->Seek(i * kBlockSize, SEEK_SET), i * kBlockSize);
- ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
- ASSERT_EQ(block, base_blocks_[i]);
- }
-
- // Test that we can read back our modified blocks.
- std::string block(kBlockSize, 0);
- ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
- ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
- ASSERT_EQ(block, base_blocks_[0]);
-
- ASSERT_EQ(reader->Seek(5 * kBlockSize, SEEK_SET), 5 * kBlockSize);
- ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
- ASSERT_EQ(block, MakeNewBlockString());
-
- std::string two_blocks(kBlockSize * 2, 0x7f);
- std::string zeroes(kBlockSize * 2, 0);
- ASSERT_EQ(reader->Seek(7 * kBlockSize, SEEK_SET), 7 * kBlockSize);
- ASSERT_EQ(reader->Read(two_blocks.data(), two_blocks.size()), two_blocks.size());
- ASSERT_EQ(two_blocks, zeroes);
- }
-
- void TestByteReads(ISnapshotWriter* writer) {
- auto reader = writer->OpenReader();
- ASSERT_NE(reader, nullptr);
-
- std::string blob(kBlockSize * 3, 'x');
-
- // Test that we can read in the middle of a block.
- static constexpr size_t kOffset = 970;
- off64_t offset = 3 * kBlockSize + kOffset;
- ASSERT_EQ(reader->Seek(0, SEEK_SET), 0);
- ASSERT_EQ(reader->Seek(offset, SEEK_CUR), offset);
- ASSERT_EQ(reader->Read(blob.data(), blob.size()), blob.size());
- ASSERT_EQ(blob.substr(0, 100), base_blocks_[0].substr(kOffset, 100));
- ASSERT_EQ(blob.substr(kBlockSize - kOffset, kBlockSize), base_blocks_[4]);
- ASSERT_EQ(blob.substr(kBlockSize * 2 - kOffset, 100), MakeNewBlockString().substr(0, 100));
- ASSERT_EQ(blob.substr(blob.size() - kOffset), base_blocks_[6].substr(0, kOffset));
-
- // Pull a random byte from the compressed block.
- char value;
- offset = 5 * kBlockSize + 1000;
- ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
- ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
- ASSERT_EQ(value, MakeNewBlockString()[1000]);
- }
-
- void TestReads(ISnapshotWriter* writer) {
- ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
- ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
- }
-
- std::string MakeNewBlockString() {
- std::string new_block = "This is a new block";
- new_block.resize(kBlockSize / 2, '*');
- new_block.resize(kBlockSize, '!');
- return new_block;
- }
-
- std::unique_ptr<TemporaryFile> base_;
- std::unique_ptr<TemporaryFile> cow_;
- std::vector<std::string> base_blocks_;
-};
-
-TEST_F(OfflineSnapshotTest, CompressedSnapshot) {
- CowOptions options;
- options.compression = "gz";
- options.max_blocks = {kBlockCount};
- options.scratch_space = false;
-
- unique_fd cow_fd(dup(cow_->fd));
- ASSERT_GE(cow_fd, 0);
-
- auto writer = std::make_unique<CompressedSnapshotWriter>(options);
- writer->SetSourceDevice(base_->path);
- ASSERT_TRUE(writer->SetCowDevice(std::move(cow_fd)));
- ASSERT_TRUE(writer->Initialize());
- ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
- ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 712eafba9..372373086 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -84,67 +84,19 @@ bool SnapshotMergeStats::Start() {
return WriteState();
}
-void SnapshotMergeStats::set_state(android::snapshot::UpdateState state, bool using_compression) {
+void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
report_.set_state(state);
- report_.set_compression_enabled(using_compression);
}
void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
report_.set_cow_file_size(cow_file_size);
+ WriteState();
}
uint64_t SnapshotMergeStats::cow_file_size() {
return report_.cow_file_size();
}
-void SnapshotMergeStats::set_total_cow_size_bytes(uint64_t bytes) {
- report_.set_total_cow_size_bytes(bytes);
-}
-
-void SnapshotMergeStats::set_estimated_cow_size_bytes(uint64_t bytes) {
- report_.set_estimated_cow_size_bytes(bytes);
-}
-
-uint64_t SnapshotMergeStats::total_cow_size_bytes() {
- return report_.total_cow_size_bytes();
-}
-
-uint64_t SnapshotMergeStats::estimated_cow_size_bytes() {
- return report_.estimated_cow_size_bytes();
-}
-
-void SnapshotMergeStats::set_boot_complete_time_ms(uint32_t ms) {
- report_.set_boot_complete_time_ms(ms);
-}
-
-uint32_t SnapshotMergeStats::boot_complete_time_ms() {
- return report_.boot_complete_time_ms();
-}
-
-void SnapshotMergeStats::set_boot_complete_to_merge_start_time_ms(uint32_t ms) {
- report_.set_boot_complete_to_merge_start_time_ms(ms);
-}
-
-uint32_t SnapshotMergeStats::boot_complete_to_merge_start_time_ms() {
- return report_.boot_complete_to_merge_start_time_ms();
-}
-
-void SnapshotMergeStats::set_merge_failure_code(MergeFailureCode code) {
- report_.set_merge_failure_code(code);
-}
-
-MergeFailureCode SnapshotMergeStats::merge_failure_code() {
- return report_.merge_failure_code();
-}
-
-void SnapshotMergeStats::set_source_build_fingerprint(const std::string& fingerprint) {
- report_.set_source_build_fingerprint(fingerprint);
-}
-
-std::string SnapshotMergeStats::source_build_fingerprint() {
- return report_.source_build_fingerprint();
-}
-
class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
public:
SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
deleted file mode 100644
index a8d5b8a1a..000000000
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <libsnapshot/snapshot_stub.h>
-
-#include <android-base/logging.h>
-
-#include <libsnapshot/snapshot_stats.h>
-
-using android::fs_mgr::CreateLogicalPartitionParams;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::FileDescriptor;
-
-namespace android::snapshot {
-
-std::unique_ptr<ISnapshotManager> SnapshotManagerStub::New() {
- return std::make_unique<SnapshotManagerStub>();
-}
-
-bool SnapshotManagerStub::BeginUpdate() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::CancelUpdate() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::FinishedSnapshotWrites(bool) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::InitiateMerge() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-UpdateState SnapshotManagerStub::ProcessUpdateState(const std::function<bool()>&,
- const std::function<bool()>&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return UpdateState::None;
-}
-
-UpdateState SnapshotManagerStub::GetUpdateState(double*) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return UpdateState::None;
-}
-
-Return SnapshotManagerStub::CreateUpdateSnapshots(const DeltaArchiveManifest&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return Return::Error();
-}
-
-bool SnapshotManagerStub::MapUpdateSnapshot(const CreateLogicalPartitionParams&, std::string*) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::UnmapUpdateSnapshot(const std::string&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::NeedSnapshotsInFirstStageMount() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::CreateLogicalAndSnapshotPartitions(const std::string&,
- const std::chrono::milliseconds&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::HandleImminentDataWipe(const std::function<void()>&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::FinishMergeInRecovery() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return CreateResult::ERROR;
-}
-
-CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices(
- const std::unique_ptr<AutoDevice>&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return CreateResult::ERROR;
-}
-
-bool SnapshotManagerStub::Dump(std::ostream&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-std::unique_ptr<AutoDevice> SnapshotManagerStub::EnsureMetadataMounted() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return nullptr;
-}
-
-bool SnapshotManagerStub::UpdateUsesCompression() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-class SnapshotMergeStatsStub : public ISnapshotMergeStats {
- bool Start() override { return false; }
- void set_state(android::snapshot::UpdateState, bool) override {}
- void set_cow_file_size(uint64_t) override {}
- uint64_t cow_file_size() override { return 0; }
- std::unique_ptr<Result> Finish() override { return nullptr; }
- void set_total_cow_size_bytes(uint64_t) override {}
- void set_estimated_cow_size_bytes(uint64_t) override {}
- uint64_t total_cow_size_bytes() override { return 0; }
- uint64_t estimated_cow_size_bytes() override { return 0; }
- void set_boot_complete_time_ms(uint32_t) override {}
- uint32_t boot_complete_time_ms() override { return 0; }
- void set_boot_complete_to_merge_start_time_ms(uint32_t) override {}
- uint32_t boot_complete_to_merge_start_time_ms() override { return 0; }
- void set_merge_failure_code(MergeFailureCode) override {}
- MergeFailureCode merge_failure_code() override { return MergeFailureCode::Ok; }
- void set_source_build_fingerprint(const std::string&) override {}
- std::string source_build_fingerprint() override { return {}; }
- bool WriteState() override { return false; }
-};
-
-ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
- static SnapshotMergeStatsStub snapshot_merge_stats;
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return &snapshot_merge_stats;
-}
-
-std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
- const CreateLogicalPartitionParams&, const std::optional<std::string>&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return nullptr;
-}
-
-bool SnapshotManagerStub::MapAllSnapshots(const std::chrono::milliseconds&) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-bool SnapshotManagerStub::UnmapAllSnapshots() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return false;
-}
-
-void SnapshotManagerStub::UpdateCowStats(ISnapshotMergeStats*) {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
-}
-
-auto SnapshotManagerStub::ReadMergeFailureCode() -> MergeFailureCode {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return MergeFailureCode::Ok;
-}
-
-std::string SnapshotManagerStub::ReadSourceBuildFingerprint() {
- LOG(ERROR) << __FUNCTION__ << " should never be called.";
- return {};
-}
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 60186434a..dce2c41f7 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -12,11 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <libsnapshot/cow_format.h>
#include <libsnapshot/snapshot.h>
#include <fcntl.h>
-#include <signal.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -31,7 +29,6 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <fs_mgr/file_wait.h>
#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
@@ -42,14 +39,8 @@
#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/test_helpers.h>
-#include "partition_cow_creator.h"
#include "utility.h"
-// Mock classes are not used. Header included to ensure mocked class definition aligns with the
-// class itself.
-#include <libsnapshot/mock_device_info.h>
-#include <libsnapshot/mock_snapshot.h>
-
namespace android {
namespace snapshot {
@@ -121,8 +112,6 @@ class SnapshotTest : public ::testing::Test {
image_manager_ = sm->image_manager();
test_device->set_slot_suffix("_a");
-
- sm->set_use_first_stage_snapuserd(false);
}
void CleanupTestArtifacts() {
@@ -148,7 +137,6 @@ class SnapshotTest : public ::testing::Test {
"base-device",
"test_partition_b",
"test_partition_b-base",
- "test_partition_b-base",
};
for (const auto& partition : partitions) {
DeleteDevice(partition);
@@ -183,22 +171,12 @@ class SnapshotTest : public ::testing::Test {
}
// If |path| is non-null, the partition will be mapped after creation.
- bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr,
- const std::optional<std::string> group = {}) {
+ bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
TestPartitionOpener opener(fake_super);
auto builder = MetadataBuilder::New(opener, "super", 0);
if (!builder) return false;
- std::string partition_group = std::string(android::fs_mgr::kDefaultGroup);
- if (group) {
- partition_group = *group;
- }
- return CreatePartition(builder.get(), name, size, path, partition_group);
- }
-
- bool CreatePartition(MetadataBuilder* builder, const std::string& name, uint64_t size,
- std::string* path, const std::string& group) {
- auto partition = builder->AddPartition(name, group, 0);
+ auto partition = builder->AddPartition(name, 0);
if (!partition) return false;
if (!builder->ResizePartition(partition, size)) {
return false;
@@ -207,8 +185,6 @@ class SnapshotTest : public ::testing::Test {
// Update the source slot.
auto metadata = builder->Export();
if (!metadata) return false;
-
- TestPartitionOpener opener(fake_super);
if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
return false;
}
@@ -225,62 +201,45 @@ class SnapshotTest : public ::testing::Test {
return CreateLogicalPartition(params, path);
}
- AssertionResult MapUpdateSnapshot(const std::string& name,
- std::unique_ptr<ISnapshotWriter>* writer) {
+ bool MapUpdatePartitions() {
TestPartitionOpener opener(fake_super);
- CreateLogicalPartitionParams params{
- .block_device = fake_super,
- .metadata_slot = 1,
- .partition_name = name,
- .timeout_ms = 10s,
- .partition_opener = &opener,
- };
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ if (!builder) return false;
- auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
- auto result = sm->OpenSnapshotWriter(params, {old_partition});
- if (!result) {
- return AssertionFailure() << "Cannot open snapshot for writing: " << name;
- }
- if (!result->Initialize()) {
- return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
- }
+ auto metadata = builder->Export();
+ if (!metadata) return false;
- if (writer) {
- *writer = std::move(result);
+ // Update the destination slot, mark it as updated.
+ if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
+ return false;
}
- return AssertionSuccess();
- }
-
- AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
- TestPartitionOpener opener(fake_super);
- CreateLogicalPartitionParams params{
- .block_device = fake_super,
- .metadata_slot = 1,
- .partition_name = name,
- .timeout_ms = 10s,
- .partition_opener = &opener,
- };
- auto result = sm->MapUpdateSnapshot(params, path);
- if (!result) {
- return AssertionFailure() << "Cannot open snapshot for writing: " << name;
+ for (const auto& partition : metadata->partitions) {
+ CreateLogicalPartitionParams params = {
+ .block_device = fake_super,
+ .metadata = metadata.get(),
+ .partition = &partition,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ .device_name = GetPartitionName(partition) + "-base",
+ };
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
+ return false;
+ }
}
- return AssertionSuccess();
+ return true;
}
AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
AssertionResult res = AssertionSuccess();
if (!(res = DeleteDevice(snapshot))) return res;
- if (!sm->UnmapDmUserDevice(snapshot)) {
- return AssertionFailure() << "Cannot delete dm-user device for " << snapshot;
- }
if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
if (!(res = DeleteDevice(snapshot + "-cow"))) return res;
if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) {
return AssertionFailure() << "Cannot unmap image " << snapshot << "-cow-img";
}
if (!(res = DeleteDevice(snapshot + "-base"))) return res;
- if (!(res = DeleteDevice(snapshot + "-src"))) return res;
return AssertionSuccess();
}
@@ -300,7 +259,7 @@ class SnapshotTest : public ::testing::Test {
if (!map_res) {
return map_res;
}
- if (!InitializeKernelCow(cow_device)) {
+ if (!InitializeCow(cow_device)) {
return AssertionFailure() << "Cannot zero fill " << cow_device;
}
if (!sm->UnmapCowImage(name)) {
@@ -321,59 +280,37 @@ class SnapshotTest : public ::testing::Test {
// Prepare A/B slot for a partition named "test_partition".
AssertionResult PrepareOneSnapshot(uint64_t device_size,
- std::unique_ptr<ISnapshotWriter>* writer = nullptr) {
- lock_ = nullptr;
-
- DeltaArchiveManifest manifest;
-
- auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();
- dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
- dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
-
- auto group = dynamic_partition_metadata->add_groups();
- group->set_name("group");
- group->set_size(device_size * 2);
- group->add_partition_names("test_partition");
-
- auto pu = manifest.add_partitions();
- pu->set_partition_name("test_partition");
- pu->set_estimate_cow_size(device_size);
- SetSize(pu, device_size);
-
- auto extent = pu->add_operations()->add_dst_extents();
- extent->set_start_block(0);
- if (device_size) {
- extent->set_num_blocks(device_size / manifest.block_size());
+ std::string* out_snap_device = nullptr) {
+ std::string base_device, cow_device, snap_device;
+ if (!CreatePartition("test_partition_a", device_size)) {
+ return AssertionFailure();
}
-
- TestPartitionOpener opener(fake_super);
- auto builder = MetadataBuilder::New(opener, "super", 0);
- if (!builder) {
- return AssertionFailure() << "Failed to open MetadataBuilder";
+ if (!MapUpdatePartitions()) {
+ return AssertionFailure();
}
- builder->AddGroup("group_a", 16_GiB);
- builder->AddGroup("group_b", 16_GiB);
- if (!CreatePartition(builder.get(), "test_partition_a", device_size, nullptr, "group_a")) {
- return AssertionFailure() << "Failed create test_partition_a";
+ if (!dm_.GetDmDevicePathByName("test_partition_b-base", &base_device)) {
+ return AssertionFailure();
}
-
- if (!sm->CreateUpdateSnapshots(manifest)) {
- return AssertionFailure() << "Failed to create update snapshots";
+ SnapshotStatus status;
+ status.set_name("test_partition_b");
+ status.set_device_size(device_size);
+ status.set_snapshot_size(device_size);
+ status.set_cow_file_size(device_size);
+ if (!sm->CreateSnapshot(lock_.get(), &status)) {
+ return AssertionFailure();
}
-
- if (writer) {
- auto res = MapUpdateSnapshot("test_partition_b", writer);
- if (!res) {
- return res;
- }
- } else if (!IsCompressionEnabled()) {
- std::string ignore;
- if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
- return AssertionFailure() << "Failed to map test_partition_b";
- }
+ if (!CreateCowImage("test_partition_b")) {
+ return AssertionFailure();
}
- if (!AcquireLock()) {
- return AssertionFailure() << "Failed to acquire lock";
+ if (!MapCowImage("test_partition_b", 10s, &cow_device)) {
+ return AssertionFailure();
+ }
+ if (!sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
+ &snap_device)) {
+ return AssertionFailure();
+ }
+ if (out_snap_device) {
+ *out_snap_device = std::move(snap_device);
}
return AssertionSuccess();
}
@@ -382,38 +319,20 @@ class SnapshotTest : public ::testing::Test {
AssertionResult SimulateReboot() {
lock_ = nullptr;
if (!sm->FinishedSnapshotWrites(false)) {
- return AssertionFailure() << "Failed to finish snapshot writes";
+ return AssertionFailure();
}
- if (!sm->UnmapUpdateSnapshot("test_partition_b")) {
- return AssertionFailure() << "Failed to unmap COW for test_partition_b";
+ if (!dm_.DeleteDevice("test_partition_b")) {
+ return AssertionFailure();
}
- if (!dm_.DeleteDeviceIfExists("test_partition_b")) {
- return AssertionFailure() << "Failed to delete test_partition_b";
+ if (!DestroyLogicalPartition("test_partition_b-base")) {
+ return AssertionFailure();
}
- if (!dm_.DeleteDeviceIfExists("test_partition_b-base")) {
- return AssertionFailure() << "Failed to destroy test_partition_b-base";
+ if (!sm->UnmapCowImage("test_partition_b")) {
+ return AssertionFailure();
}
return AssertionSuccess();
}
- std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(
- const std::string& slot_suffix = "_a") {
- auto info = new TestDeviceInfo(fake_super, slot_suffix);
- return NewManagerForFirstStageMount(info);
- }
-
- std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) {
- info->set_first_stage_init(true);
- auto init = SnapshotManager::NewForFirstStageMount(info);
- if (!init) {
- return nullptr;
- }
- init->SetUeventRegenCallback([](const std::string& device) -> bool {
- return android::fs_mgr::WaitForFile(device, snapshot_timeout_);
- });
- return init;
- }
-
static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
@@ -424,21 +343,13 @@ class SnapshotTest : public ::testing::Test {
TEST_F(SnapshotTest, CreateSnapshot) {
ASSERT_TRUE(AcquireLock());
- PartitionCowCreator cow_creator;
- cow_creator.compression_enabled = IsCompressionEnabled();
- if (cow_creator.compression_enabled) {
- cow_creator.compression_algorithm = "gz";
- } else {
- cow_creator.compression_algorithm = "none";
- }
-
static const uint64_t kDeviceSize = 1024 * 1024;
SnapshotStatus status;
status.set_name("test-snapshot");
status.set_device_size(kDeviceSize);
status.set_snapshot_size(kDeviceSize);
status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
ASSERT_TRUE(CreateCowImage("test-snapshot"));
std::vector<std::string> snapshots;
@@ -453,8 +364,6 @@ TEST_F(SnapshotTest, CreateSnapshot) {
ASSERT_EQ(status.state(), SnapshotState::CREATED);
ASSERT_EQ(status.device_size(), kDeviceSize);
ASSERT_EQ(status.snapshot_size(), kDeviceSize);
- ASSERT_EQ(status.compression_enabled(), cow_creator.compression_enabled);
- ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);
}
ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
@@ -465,16 +374,38 @@ TEST_F(SnapshotTest, CreateSnapshot) {
TEST_F(SnapshotTest, MapSnapshot) {
ASSERT_TRUE(AcquireLock());
- PartitionCowCreator cow_creator;
- cow_creator.compression_enabled = IsCompressionEnabled();
-
static const uint64_t kDeviceSize = 1024 * 1024;
SnapshotStatus status;
status.set_name("test-snapshot");
status.set_device_size(kDeviceSize);
status.set_snapshot_size(kDeviceSize);
status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
+ ASSERT_TRUE(CreateCowImage("test-snapshot"));
+
+ std::string base_device;
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+
+ std::string cow_device;
+ ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
+
+ std::string snap_device;
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
+ ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
+}
+
+TEST_F(SnapshotTest, MapPartialSnapshot) {
+ ASSERT_TRUE(AcquireLock());
+
+ static const uint64_t kSnapshotSize = 1024 * 1024;
+ static const uint64_t kDeviceSize = 1024 * 1024 * 2;
+ SnapshotStatus status;
+ status.set_name("test-snapshot");
+ status.set_device_size(kDeviceSize);
+ status.set_snapshot_size(kSnapshotSize);
+ status.set_cow_file_size(kSnapshotSize);
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
ASSERT_TRUE(CreateCowImage("test-snapshot"));
std::string base_device;
@@ -499,7 +430,8 @@ TEST_F(SnapshotTest, NoMergeBeforeReboot) {
TEST_F(SnapshotTest, CleanFirstStageMount) {
// If there's no update in progress, there should be no first-stage mount
// needed.
- auto sm = NewManagerForFirstStageMount();
+ TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+ auto sm = SnapshotManager::NewForFirstStageMount(info);
ASSERT_NE(sm, nullptr);
ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
}
@@ -508,7 +440,8 @@ TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// We didn't change the slot, so we shouldn't need snapshots.
- auto sm = NewManagerForFirstStageMount();
+ TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+ auto sm = SnapshotManager::NewForFirstStageMount(info);
ASSERT_NE(sm, nullptr);
ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
@@ -520,30 +453,32 @@ TEST_F(SnapshotTest, Merge) {
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
+ std::string snap_device;
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &snap_device));
- std::unique_ptr<ISnapshotWriter> writer;
- ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
+ std::string test_string = "This is a test string.";
+ {
+ unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
+ }
+
+ // Note: we know there is no inner/outer dm device since we didn't request
+ // a linear segment.
+ DeviceMapper::TargetInfo target;
+ ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
+ ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
// Release the lock.
lock_ = nullptr;
- std::string test_string = "This is a test string.";
- test_string.resize(writer->options().block_size);
- ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
- ASSERT_TRUE(writer->Finalize());
- writer = nullptr;
-
// Done updating.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
- ASSERT_TRUE(sm->UnmapUpdateSnapshot("test_partition_b"));
-
test_device->set_slot_suffix("_b");
- ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
ASSERT_TRUE(sm->InitiateMerge());
// The device should have been switched to a snapshot-merge target.
- DeviceMapper::TargetInfo target;
ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
@@ -559,7 +494,7 @@ TEST_F(SnapshotTest, Merge) {
// Test that we can read back the string we wrote to the snapshot. Note
// that the base device is gone now. |snap_device| contains the correct
// partition.
- unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
+ unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
std::string buffer(test_string.size(), '\0');
@@ -574,7 +509,7 @@ TEST_F(SnapshotTest, FirstStageMountAndMerge) {
ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
ASSERT_TRUE(SimulateReboot());
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -585,14 +520,10 @@ TEST_F(SnapshotTest, FirstStageMountAndMerge) {
SnapshotStatus status;
ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
ASSERT_EQ(status.state(), SnapshotState::CREATED);
- if (IsCompressionEnabled()) {
- ASSERT_EQ(status.compression_algorithm(), "gz");
- } else {
- ASSERT_EQ(status.compression_algorithm(), "none");
- }
DeviceMapper::TargetInfo target;
- ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
+ auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+ ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
}
@@ -607,7 +538,7 @@ TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
FormatFakeSuper();
ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -619,7 +550,8 @@ TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
// We should not get a snapshot device now.
DeviceMapper::TargetInfo target;
- ASSERT_FALSE(init->IsSnapshotDevice("test_partition_b", &target));
+ auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+ ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
// We should see a cancelled update as well.
lock_ = nullptr;
@@ -633,7 +565,7 @@ TEST_F(SnapshotTest, FlashSuperDuringMerge) {
ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
ASSERT_TRUE(SimulateReboot());
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -650,8 +582,8 @@ TEST_F(SnapshotTest, FlashSuperDuringMerge) {
// Because the status is Merging, we must call ProcessUpdateState, which should
// detect a cancelled update.
- ASSERT_EQ(init->ProcessUpdateState(), UpdateState::Cancelled);
- ASSERT_EQ(init->GetUpdateState(), UpdateState::None);
+ ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
+ ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
}
TEST_F(SnapshotTest, UpdateBootControlHal) {
@@ -679,18 +611,6 @@ TEST_F(SnapshotTest, UpdateBootControlHal) {
ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
}
-TEST_F(SnapshotTest, MergeFailureCode) {
- ASSERT_TRUE(AcquireLock());
-
- ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed,
- MergeFailureCode::ListSnapshots));
- ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
-
- SnapshotUpdateStatus status = sm->ReadSnapshotUpdateStatus(lock_.get());
- ASSERT_EQ(status.state(), UpdateState::MergeFailed);
- ASSERT_EQ(status.merge_failure_code(), MergeFailureCode::ListSnapshots);
-}
-
enum class Request { UNKNOWN, LOCK_SHARED, LOCK_EXCLUSIVE, UNLOCK, EXIT };
std::ostream& operator<<(std::ostream& os, Request request) {
switch (request) {
@@ -854,15 +774,11 @@ class SnapshotUpdateTest : public SnapshotTest {
opener_ = std::make_unique<TestPartitionOpener>(fake_super);
- auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
- dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
- dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
-
// Create a fake update package metadata.
// Not using full name "system", "vendor", "product" because these names collide with the
// mapped partitions on the running device.
// Each test modifies manifest_ slightly to indicate changes to the partition layout.
- group_ = dynamic_partition_metadata->add_groups();
+ group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
group_->set_name("group");
group_->set_size(kGroupSize);
group_->add_partition_names("sys");
@@ -870,15 +786,12 @@ class SnapshotUpdateTest : public SnapshotTest {
group_->add_partition_names("prd");
sys_ = manifest_.add_partitions();
sys_->set_partition_name("sys");
- sys_->set_estimate_cow_size(2_MiB);
SetSize(sys_, 3_MiB);
vnd_ = manifest_.add_partitions();
vnd_->set_partition_name("vnd");
- vnd_->set_estimate_cow_size(2_MiB);
SetSize(vnd_, 3_MiB);
prd_ = manifest_.add_partitions();
prd_->set_partition_name("prd");
- prd_->set_estimate_cow_size(2_MiB);
SetSize(prd_, 3_MiB);
// Initialize source partition metadata using |manifest_|.
@@ -974,87 +887,49 @@ class SnapshotUpdateTest : public SnapshotTest {
return AssertionSuccess();
}
- AssertionResult MapOneUpdateSnapshot(const std::string& name) {
- if (IsCompressionEnabled()) {
- std::unique_ptr<ISnapshotWriter> writer;
- return MapUpdateSnapshot(name, &writer);
- } else {
- std::string path;
- return MapUpdateSnapshot(name, &path);
+ AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path = nullptr) {
+ std::string real_path;
+ if (!sm->MapUpdateSnapshot(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = 1,
+ .partition_name = name,
+ .timeout_ms = 10s,
+ .partition_opener = opener_.get(),
+ },
+ &real_path)) {
+ return AssertionFailure() << "Unable to map snapshot " << name;
}
- }
-
- AssertionResult WriteSnapshotAndHash(const std::string& name) {
- if (IsCompressionEnabled()) {
- std::unique_ptr<ISnapshotWriter> writer;
- auto res = MapUpdateSnapshot(name, &writer);
- if (!res) {
- return res;
- }
- if (!WriteRandomData(writer.get(), &hashes_[name])) {
- return AssertionFailure() << "Unable to write random data to snapshot " << name;
- }
- if (!writer->Finalize()) {
- return AssertionFailure() << "Unable to finalize COW for " << name;
- }
- } else {
- std::string path;
- auto res = MapUpdateSnapshot(name, &path);
- if (!res) {
- return res;
- }
- if (!WriteRandomData(path, std::nullopt, &hashes_[name])) {
- return AssertionFailure() << "Unable to write random data to snapshot " << name;
- }
+ if (path) {
+ *path = real_path;
}
-
- // Make sure updates to one device are seen by all devices.
- sync();
-
- return AssertionSuccess() << "Written random data to snapshot " << name
- << ", hash: " << hashes_[name];
+ return AssertionSuccess() << "Mapped snapshot " << name << " to " << real_path;
}
- // Generate a snapshot that moves all the upper blocks down to the start.
- // It doesn't really matter the order, we just want copies that reference
- // blocks that won't exist if the partition shrinks.
- AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {
- std::unique_ptr<ISnapshotWriter> writer;
- if (auto res = MapUpdateSnapshot(name, &writer); !res) {
+ AssertionResult WriteSnapshotAndHash(const std::string& name,
+ std::optional<size_t> size = std::nullopt) {
+ std::string path;
+ auto res = MapUpdateSnapshot(name, &path);
+ if (!res) {
return res;
}
- if (!writer->options().max_blocks || !*writer->options().max_blocks) {
- return AssertionFailure() << "No max blocks set for " << name << " writer";
- }
- uint64_t src_block = (old_size / writer->options().block_size) - 1;
- uint64_t dst_block = 0;
- uint64_t max_blocks = *writer->options().max_blocks;
- while (dst_block < max_blocks && dst_block < src_block) {
- if (!writer->AddCopy(dst_block, src_block)) {
- return AssertionFailure() << "Unable to add copy for " << name << " for blocks "
- << src_block << ", " << dst_block;
- }
- dst_block++;
- src_block--;
- }
- if (!writer->Finalize()) {
- return AssertionFailure() << "Unable to finalize writer for " << name;
- }
+ std::string size_string = size ? (std::to_string(*size) + " bytes") : "";
- auto hash = HashSnapshot(writer.get());
- if (hash.empty()) {
- return AssertionFailure() << "Unable to hash snapshot writer for " << name;
+ if (!WriteRandomData(path, size, &hashes_[name])) {
+ return AssertionFailure() << "Unable to write " << size_string << " to " << path
+ << " for partition " << name;
}
- hashes_[name] = hash;
- return AssertionSuccess();
+ return AssertionSuccess() << "Written " << size_string << " to " << path
+ << " for snapshot partition " << name
+ << ", hash: " << hashes_[name];
}
AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
"prd_b"}) {
for (const auto& name : names) {
- auto res = MapOneUpdateSnapshot(name);
+ auto res = MapUpdateSnapshot(name);
if (!res) {
return res;
}
@@ -1102,17 +977,11 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
}
- // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
- // fit in super, but not |prd|.
+ // Grow all partitions.
constexpr uint64_t partition_size = 3788_KiB;
SetSize(sys_, partition_size);
SetSize(vnd_, partition_size);
- SetSize(prd_, 18_MiB);
-
- // Make sure |prd| does not fit in super at all. On VABC, this means we
- // fake an extra large COW for |vnd| to fill up super.
- vnd_->set_estimate_cow_size(30_MiB);
- prd_->set_estimate_cow_size(30_MiB);
+ SetSize(prd_, partition_size);
AddOperationForPartitions();
@@ -1129,7 +998,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
// Write some data to target partitions.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
+ ASSERT_TRUE(WriteSnapshotAndHash(name, partition_size));
}
// Assert that source partitions aren't affected.
@@ -1143,7 +1012,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1158,138 +1027,8 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
// Initiate the merge and wait for it to be completed.
ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
- {
- // We should have started in SECOND_PHASE since nothing shrinks.
- ASSERT_TRUE(AcquireLock());
- auto local_lock = std::move(lock_);
- auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
- ASSERT_EQ(status.merge_phase(), MergePhase::SECOND_PHASE);
- }
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
- // Make sure the second phase ran and deleted snapshots.
- {
- ASSERT_TRUE(AcquireLock());
- auto local_lock = std::move(lock_);
- std::vector<std::string> snapshots;
- ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));
- ASSERT_TRUE(snapshots.empty());
- }
-
- // Check that the target partitions have the same content after the merge.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name))
- << "Content of " << name << " changes after the merge";
- }
-}
-
-// Test that shrinking and growing partitions at the same time is handled
-// correctly in VABC.
-TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
- if (!IsCompressionEnabled()) {
- // b/179111359
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
- }
-
- // OTA client blindly unmaps all partitions that are possibly mapped.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
- }
-
- auto old_sys_size = GetSize(sys_);
- auto old_prd_size = GetSize(prd_);
-
- // Grow |sys| but shrink |prd|.
- SetSize(sys_, old_sys_size * 2);
- sys_->set_estimate_cow_size(8_MiB);
- SetSize(prd_, old_prd_size / 2);
- prd_->set_estimate_cow_size(1_MiB);
-
- AddOperationForPartitions();
-
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
- // Check that the old partition sizes were saved correctly.
- {
- ASSERT_TRUE(AcquireLock());
- auto local_lock = std::move(lock_);
-
- SnapshotStatus status;
- ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), "prd_b", &status));
- ASSERT_EQ(status.old_partition_size(), 3145728);
- ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), "sys_b", &status));
- ASSERT_EQ(status.old_partition_size(), 3145728);
- }
-
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
- ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
- ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
-
- sync();
-
- // Assert that source partitions aren't affected.
- for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name));
- }
-
- ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
- // Simulate shutting down the device.
- ASSERT_TRUE(UnmapAll());
-
- // After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
- ASSERT_NE(init, nullptr);
- ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
- auto indicator = sm->GetRollbackIndicatorPath();
- ASSERT_NE(access(indicator.c_str(), R_OK), 0);
-
- // Check that the target partitions have the same content.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name));
- }
-
- // Initiate the merge and wait for it to be completed.
- ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
- {
- // Check that the merge phase is FIRST_PHASE until at least one call
- // to ProcessUpdateState() occurs.
- ASSERT_TRUE(AcquireLock());
- auto local_lock = std::move(lock_);
- auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
- ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
- }
-
- // Simulate shutting down the device and creating partitions again.
- ASSERT_TRUE(UnmapAll());
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
- // Check that we used the correct types after rebooting mid-merge.
- DeviceMapper::TargetInfo target;
- ASSERT_TRUE(init->IsSnapshotDevice("prd_b", &target));
- ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
- ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
- ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
- ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
- ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-
- // Complete the merge.
- ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-
- // Make sure the second phase ran and deleted snapshots.
- {
- ASSERT_TRUE(AcquireLock());
- auto local_lock = std::move(lock_);
- std::vector<std::string> snapshots;
- ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));
- ASSERT_TRUE(snapshots.empty());
- }
-
// Check that the target partitions have the same content after the merge.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
ASSERT_TRUE(IsPartitionUnchanged(name))
@@ -1312,7 +1051,6 @@ TEST_F(SnapshotUpdateTest, SnapshotOldPartitions) {
SetSize(sys_, 4_MiB); // grows
SetSize(vnd_, 2_MiB); // shrinks
// prd_b is unchanged
- ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
ASSERT_EQ(4_MiB, GetSnapshotSize("sys_b").value_or(0));
}
@@ -1322,7 +1060,6 @@ TEST_F(SnapshotUpdateTest, SnapshotOldPartitions) {
TEST_F(SnapshotUpdateTest, CowPartitionDoNotTakeOldPartitions) {
SetSize(sys_, 2_MiB); // shrinks
// vnd_b and prd_b are unchanged.
- ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
auto tgt = MetadataBuilder::New(*opener_, "super", 1);
@@ -1407,7 +1144,7 @@ TEST_F(SnapshotUpdateTest, TestRollback) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1419,7 +1156,7 @@ TEST_F(SnapshotUpdateTest, TestRollback) {
// Simulate shutting down the device again.
ASSERT_TRUE(UnmapAll());
- init = NewManagerForFirstStageMount("_a");
+ init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a"));
ASSERT_NE(init, nullptr);
ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1446,11 +1183,6 @@ static std::vector<Interval> ToIntervals(const std::vector<std::unique_ptr<Exten
// Test that at the second update, old COW partition spaces are reclaimed.
TEST_F(SnapshotUpdateTest, ReclaimCow) {
- // Make sure VABC cows are small enough that they fit in fake_super.
- sys_->set_estimate_cow_size(64_KiB);
- vnd_->set_estimate_cow_size(64_KiB);
- prd_->set_estimate_cow_size(64_KiB);
-
// Execute the first update.
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
@@ -1461,7 +1193,7 @@ TEST_F(SnapshotUpdateTest, ReclaimCow) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1567,13 +1299,9 @@ TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
// Make source partitions as big as possible to force COW image to be created.
- SetSize(sys_, 10_MiB);
- SetSize(vnd_, 10_MiB);
- SetSize(prd_, 10_MiB);
- sys_->set_estimate_cow_size(12_MiB);
- vnd_->set_estimate_cow_size(12_MiB);
- prd_->set_estimate_cow_size(12_MiB);
-
+ SetSize(sys_, 5_MiB);
+ SetSize(vnd_, 5_MiB);
+ SetSize(prd_, 5_MiB);
src_ = MetadataBuilder::New(*opener_, "super", 0);
ASSERT_NE(src_, nullptr);
src_->RemoveGroupAndPartitions(group_->name() + "_a");
@@ -1601,8 +1329,8 @@ TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- // Normally we should use NewManagerForFirstStageMount, but if so,
- // "gsid.mapped_image.sys_b-cow-img" won't be set.
+ // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img"
+ // won't be set.
auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1709,7 +1437,7 @@ TEST_F(SnapshotUpdateTest, MergeInRecovery) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1723,7 +1451,7 @@ TEST_F(SnapshotUpdateTest, MergeInRecovery) {
// Simulate a reboot into recovery.
auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
test_device->set_recovery(true);
- new_sm = NewManagerForFirstStageMount(test_device.release());
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
@@ -1741,7 +1469,7 @@ TEST_F(SnapshotUpdateTest, MergeInFastboot) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1755,7 +1483,7 @@ TEST_F(SnapshotUpdateTest, MergeInFastboot) {
// Simulate a reboot into recovery.
auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
test_device->set_recovery(true);
- new_sm = NewManagerForFirstStageMount(test_device.release());
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
ASSERT_TRUE(new_sm->FinishMergeInRecovery());
@@ -1765,12 +1493,12 @@ TEST_F(SnapshotUpdateTest, MergeInFastboot) {
// Finish the merge in a normal boot.
test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
- init = NewManagerForFirstStageMount(test_device.release());
+ init = SnapshotManager::NewForFirstStageMount(test_device.release());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
init = nullptr;
test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
- new_sm = NewManagerForFirstStageMount(test_device.release());
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
}
@@ -1789,12 +1517,12 @@ TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
// Simulate a reboot into recovery.
auto test_device = new TestDeviceInfo(fake_super, "_b");
test_device->set_recovery(true);
- auto new_sm = NewManagerForFirstStageMount(test_device);
+ auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below.
MountMetadata();
- EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+ EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
EXPECT_TRUE(test_device->IsSlotUnbootable(1));
EXPECT_FALSE(test_device->IsSlotUnbootable(0));
}
@@ -1814,7 +1542,7 @@ TEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {
// Simulate a rollback, with reboot into recovery.
auto test_device = new TestDeviceInfo(fake_super, "_a");
test_device->set_recovery(true);
- auto new_sm = NewManagerForFirstStageMount(test_device);
+ auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
@@ -1842,7 +1570,7 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
// Simulate a reboot into recovery.
auto test_device = new TestDeviceInfo(fake_super, "_b");
test_device->set_recovery(true);
- auto new_sm = NewManagerForFirstStageMount(test_device);
+ auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below.
@@ -1853,68 +1581,7 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
// Now reboot into new slot.
test_device = new TestDeviceInfo(fake_super, "_b");
- auto init = NewManagerForFirstStageMount(test_device);
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
- // Verify that we are on the downgraded build.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name)) << name;
- }
-}
-
-// Test update package that requests data wipe.
-TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
- AddOperationForPartitions();
-
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
- // Write some data to target partitions.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
- }
-
- // Create a stale snapshot that should not exist.
- {
- ASSERT_TRUE(AcquireLock());
-
- PartitionCowCreator cow_creator = {
- .compression_enabled = IsCompressionEnabled(),
- .compression_algorithm = IsCompressionEnabled() ? "gz" : "none",
- };
- SnapshotStatus status;
- status.set_name("sys_a");
- status.set_device_size(1_MiB);
- status.set_snapshot_size(2_MiB);
- status.set_cow_partition_size(2_MiB);
-
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
- lock_ = nullptr;
-
- ASSERT_TRUE(sm->EnsureImageManager());
- ASSERT_TRUE(sm->image_manager()->CreateBackingImage("sys_a", 1_MiB, 0));
- }
-
- ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
-
- // Simulate shutting down the device.
- ASSERT_TRUE(UnmapAll());
-
- // Simulate a reboot into recovery.
- auto test_device = new TestDeviceInfo(fake_super, "_b");
- test_device->set_recovery(true);
- auto new_sm = NewManagerForFirstStageMount(test_device);
-
- ASSERT_TRUE(new_sm->HandleImminentDataWipe());
- // Manually mount metadata so that we can call GetUpdateState() below.
- MountMetadata();
- EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
- ASSERT_FALSE(test_device->IsSlotUnbootable(1));
- ASSERT_FALSE(test_device->IsSlotUnbootable(0));
-
- // Now reboot into new slot.
- test_device = new TestDeviceInfo(fake_super, "_b");
- auto init = NewManagerForFirstStageMount(test_device);
+ auto init = SnapshotManager::NewForFirstStageMount(test_device);
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
// Verify that we are on the downgraded build.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1932,8 +1599,6 @@ TEST_F(SnapshotUpdateTest, Hashtree) {
SetSize(sys_, partition_size);
AddOperation(sys_, data_size);
- sys_->set_estimate_cow_size(partition_size + data_size);
-
// Set hastree extents.
sys_->mutable_hash_tree_data_extent()->set_start_block(0);
sys_->mutable_hash_tree_data_extent()->set_num_blocks(data_size / block_size);
@@ -1953,7 +1618,7 @@ TEST_F(SnapshotUpdateTest, Hashtree) {
// Map and write some data to target partition.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b", partition_size));
// Finish update.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -1962,7 +1627,7 @@ TEST_F(SnapshotUpdateTest, Hashtree) {
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1974,10 +1639,6 @@ TEST_F(SnapshotUpdateTest, Hashtree) {
// Test for overflow bit after update
TEST_F(SnapshotUpdateTest, Overflow) {
- if (IsCompressionEnabled()) {
- GTEST_SKIP() << "No overflow bit set for userspace COWs";
- }
-
const auto actual_write_size = GetSize(sys_);
const auto declared_write_size = actual_write_size - 1_MiB;
@@ -1989,7 +1650,7 @@ TEST_F(SnapshotUpdateTest, Overflow) {
// Map and write some data to target partitions.
ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b", actual_write_size));
std::vector<android::dm::DeviceMapper::TargetInfo> table;
ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
@@ -2005,15 +1666,12 @@ TEST_F(SnapshotUpdateTest, LowSpace) {
auto userdata = std::make_unique<LowSpaceUserdata>();
ASSERT_TRUE(userdata->Init(kMaxFree));
- // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
- // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
- constexpr uint64_t partition_size = 10_MiB;
+ // Grow all partitions to 5_MiB, total 15_MiB. This requires 15 MiB of CoW space. After
+ // using the empty space in super (< 1 MiB), it uses at least 14 MiB of /userdata space.
+ constexpr uint64_t partition_size = 5_MiB;
SetSize(sys_, partition_size);
SetSize(vnd_, partition_size);
SetSize(prd_, partition_size);
- sys_->set_estimate_cow_size(partition_size);
- vnd_->set_estimate_cow_size(partition_size);
- prd_->set_estimate_cow_size(partition_size);
AddOperationForPartitions();
@@ -2023,109 +1681,7 @@ TEST_F(SnapshotUpdateTest, LowSpace) {
ASSERT_FALSE(res);
ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
ASSERT_GE(res.required_size(), 14_MiB);
- ASSERT_LT(res.required_size(), 40_MiB);
-}
-
-class AutoKill final {
- public:
- explicit AutoKill(pid_t pid) : pid_(pid) {}
- ~AutoKill() {
- if (pid_ > 0) kill(pid_, SIGKILL);
- }
-
- bool valid() const { return pid_ > 0; }
-
- private:
- pid_t pid_;
-};
-
-TEST_F(SnapshotUpdateTest, DaemonTransition) {
- if (!IsCompressionEnabled()) {
- GTEST_SKIP() << "Skipping Virtual A/B Compression test";
- }
-
- // Ensure a connection to the second-stage daemon, but use the first-stage
- // code paths thereafter.
- ASSERT_TRUE(sm->EnsureSnapuserdConnected());
- sm->set_use_first_stage_snapuserd(true);
-
- AddOperationForPartitions();
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- ASSERT_TRUE(MapUpdateSnapshots());
- ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
- ASSERT_TRUE(UnmapAll());
-
- auto init = NewManagerForFirstStageMount("_b");
- ASSERT_NE(init, nullptr);
-
- ASSERT_TRUE(init->EnsureSnapuserdConnected());
- init->set_use_first_stage_snapuserd(true);
-
- ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
- ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
- ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
-
- ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
-
- // :TODO: this is a workaround to ensure the handler list stays empty. We
- // should make this test more like actual init, and spawn two copies of
- // snapuserd, given how many other tests we now have for normal snapuserd.
- ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
- ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
- ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
-
- // The control device should have been renamed.
- ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
- ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
-}
-
-TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
- AddOperationForPartitions();
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(WriteSnapshotAndHash(name));
- }
- ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
- ASSERT_TRUE(sm->MapAllSnapshots(10s));
-
- // Read bytes back and verify they match the cache.
- ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
-
- ASSERT_TRUE(sm->UnmapAllSnapshots());
-}
-
-TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {
- AddOperationForPartitions();
-
- // Execute the update from B->A.
- test_device->set_slot_suffix("_b");
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
- std::string path;
- ASSERT_TRUE(CreateLogicalPartition(
- CreateLogicalPartitionParams{
- .block_device = fake_super,
- .metadata_slot = 0,
- .partition_name = "sys_a",
- .timeout_ms = 1s,
- .partition_opener = opener_.get(),
- },
- &path));
-
- // Hold sys_a open so it can't be unmapped.
- unique_fd fd(open(path.c_str(), O_RDONLY));
-
- // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a
- // we should simply delete the old snapshots.
- test_device->set_slot_suffix("_a");
- ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_LT(res.required_size(), 15_MiB);
}
class FlashAfterUpdateTest : public SnapshotUpdateTest,
@@ -2208,7 +1764,8 @@ TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
ASSERT_TRUE(UnmapAll());
// Simulate reboot. After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount(flashed_slot_suffix);
+ auto init = SnapshotManager::NewForFirstStageMount(
+ new TestDeviceInfo(fake_super, flashed_slot_suffix));
ASSERT_NE(init, nullptr);
if (flashed_slot && after_merge) {
@@ -2223,12 +1780,8 @@ TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
// There should be no snapshot to merge.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
- if (flashed_slot == 0 && after_merge) {
- ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
- } else {
- // update_engine calls ProcessUpdateState first -- should see Cancelled.
- ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
- }
+ // update_enigne calls ProcessUpdateState first -- should see Cancelled.
+ ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
// Next OTA calls CancelUpdate no matter what.
ASSERT_TRUE(new_sm->CancelUpdate());
@@ -2309,27 +1862,9 @@ class SnapshotTestEnvironment : public ::testing::Environment {
void TearDown() override;
private:
- bool CreateFakeSuper();
-
std::unique_ptr<IImageManager> super_images_;
};
-bool SnapshotTestEnvironment::CreateFakeSuper() {
- // Create and map the fake super partition.
- static constexpr int kImageFlags =
- IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
- if (!super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
- LOG(ERROR) << "Could not create fake super partition";
- return false;
- }
- if (!super_images_->MapImageDevice("fake-super", 10s, &fake_super)) {
- LOG(ERROR) << "Could not map fake super partition";
- return false;
- }
- test_device->set_fake_super(fake_super);
- return true;
-}
-
void SnapshotTestEnvironment::SetUp() {
// b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until
// that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test
@@ -2355,36 +1890,27 @@ void SnapshotTestEnvironment::SetUp() {
sm = SnapshotManager::New(test_device);
ASSERT_NE(nullptr, sm) << "Could not create snapshot manager";
+ // Clean up previous run.
+ MetadataMountedTest().TearDown();
+ SnapshotUpdateTest().Cleanup();
+ SnapshotTest().Cleanup();
+
// Use a separate image manager for our fake super partition.
super_images_ = IImageManager::Open("ota/test/super", 10s);
ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
- // Map the old image if one exists so we can safely unmap everything that
- // depends on it.
- bool recreate_fake_super;
- if (super_images_->BackingImageExists("fake-super")) {
- if (super_images_->IsImageMapped("fake-super")) {
- ASSERT_TRUE(super_images_->GetMappedImageDevice("fake-super", &fake_super));
- } else {
- ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super));
- }
- test_device->set_fake_super(fake_super);
- recreate_fake_super = true;
- } else {
- ASSERT_TRUE(CreateFakeSuper());
- recreate_fake_super = false;
- }
+ // Clean up any old copy.
+ DeleteBackingImage(super_images_.get(), "fake-super");
- // Clean up previous run.
- MetadataMountedTest().TearDown();
- SnapshotUpdateTest().Cleanup();
- SnapshotTest().Cleanup();
+ // Create and map the fake super partition.
+ static constexpr int kImageFlags =
+ IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
+ ASSERT_TRUE(super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags))
+ << "Could not create fake super partition";
- if (recreate_fake_super) {
- // Clean up any old copy.
- DeleteBackingImage(super_images_.get(), "fake-super");
- ASSERT_TRUE(CreateFakeSuper());
- }
+ ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super))
+ << "Could not map fake super partition";
+ test_device->set_fake_super(fake_super);
}
void SnapshotTestEnvironment::TearDown() {
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
deleted file mode 100644
index 080f3b79d..000000000
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <libsnapshot/snapshot_writer.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <payload_consumer/file_descriptor.h>
-#include "snapshot_reader.h"
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using chromeos_update_engine::FileDescriptor;
-
-ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
-
-void ISnapshotWriter::SetSourceDevice(const std::string& source_device) {
- source_device_ = {source_device};
-}
-
-borrowed_fd ISnapshotWriter::GetSourceFd() {
- if (!source_device_) {
- LOG(ERROR) << "Attempted to read from source device but none was set";
- return borrowed_fd{-1};
- }
-
- if (source_fd_ < 0) {
- source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
- if (source_fd_ < 0) {
- PLOG(ERROR) << "open " << *source_device_;
- return borrowed_fd{-1};
- }
- }
- return source_fd_;
-}
-
-CompressedSnapshotWriter::CompressedSnapshotWriter(const CowOptions& options)
- : ISnapshotWriter(options) {}
-
-bool CompressedSnapshotWriter::SetCowDevice(android::base::unique_fd&& cow_device) {
- cow_device_ = std::move(cow_device);
- cow_ = std::make_unique<CowWriter>(options_);
- return true;
-}
-
-bool CompressedSnapshotWriter::Finalize() {
- return cow_->Finalize();
-}
-
-uint64_t CompressedSnapshotWriter::GetCowSize() {
- return cow_->GetCowSize();
-}
-
-std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
- unique_fd cow_fd(dup(cow_device_.get()));
- if (cow_fd < 0) {
- PLOG(ERROR) << "dup COW device";
- return nullptr;
- }
-
- auto cow = std::make_unique<CowReader>();
- if (!cow->Parse(std::move(cow_fd))) {
- LOG(ERROR) << "Unable to read COW";
- return nullptr;
- }
-
- auto reader = std::make_unique<CompressedSnapshotReader>();
- if (!reader->SetCow(std::move(cow))) {
- LOG(ERROR) << "Unable to initialize COW reader";
- return nullptr;
- }
- if (source_device_) {
- reader->SetSourceDevice(*source_device_);
- }
-
- const auto& cow_options = options();
- if (cow_options.max_blocks) {
- reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
- }
-
- return reader;
-}
-
-bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
- return cow_->AddCopy(new_block, old_block);
-}
-
-bool CompressedSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
- size_t size) {
- return cow_->AddRawBlocks(new_block_start, data, size);
-}
-
-bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- return cow_->AddZeroBlocks(new_block_start, num_blocks);
-}
-
-bool CompressedSnapshotWriter::EmitLabel(uint64_t label) {
- return cow_->AddLabel(label);
-}
-
-bool CompressedSnapshotWriter::Initialize() {
- return cow_->Initialize(cow_device_);
-}
-
-bool CompressedSnapshotWriter::InitializeAppend(uint64_t label) {
- return cow_->InitializeAppend(cow_device_, label);
-}
-
-OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
- : ISnapshotWriter(options) {}
-
-void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
- uint64_t cow_size) {
- snapshot_fd_ = std::move(snapshot_fd);
- cow_size_ = cow_size;
-}
-
-bool OnlineKernelSnapshotWriter::Finalize() {
- if (fsync(snapshot_fd_.get()) < 0) {
- PLOG(ERROR) << "fsync";
- return false;
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
- size_t size) {
- uint64_t offset = new_block_start * options_.block_size;
- if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
- PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
- return false;
- }
- if (!android::base::WriteFully(snapshot_fd_, data, size)) {
- PLOG(ERROR) << "EmitRawBlocks write";
- return false;
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- std::string zeroes(options_.block_size, 0);
- for (uint64_t i = 0; i < num_blocks; i++) {
- if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
- return false;
- }
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
- auto source_fd = GetSourceFd();
- if (source_fd < 0) {
- return false;
- }
-
- std::string buffer(options_.block_size, 0);
- uint64_t offset = old_block * options_.block_size;
- if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
- PLOG(ERROR) << "EmitCopy read";
- return false;
- }
- return EmitRawBlocks(new_block, buffer.data(), buffer.size());
-}
-
-bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
- // Not Needed
- return true;
-}
-
-std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
- unique_fd fd(dup(snapshot_fd_.get()));
- if (fd < 0) {
- PLOG(ERROR) << "dup2 failed in OpenReader";
- return nullptr;
- }
- return std::make_unique<ReadFdFileDescriptor>(std::move(fd));
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_writer_test.cpp b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
deleted file mode 100644
index da48eb933..000000000
--- a/fs_mgr/libsnapshot/snapshot_writer_test.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <libsnapshot/snapshot.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/snapshot_writer.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android::snapshot {
-class CompressedSnapshotWriterTest : public ::testing::Test {
- public:
- static constexpr size_t BLOCK_SIZE = 4096;
-};
-
-TEST_F(CompressedSnapshotWriterTest, ReadAfterWrite) {
- TemporaryFile cow_device_file{};
- android::snapshot::CowOptions options{.block_size = BLOCK_SIZE};
- android::snapshot::CompressedSnapshotWriter snapshot_writer{options};
- snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd});
- snapshot_writer.Initialize();
- std::vector<unsigned char> buffer;
- buffer.resize(BLOCK_SIZE);
- std::fill(buffer.begin(), buffer.end(), 123);
-
- ASSERT_TRUE(snapshot_writer.AddRawBlocks(0, buffer.data(), buffer.size()));
- ASSERT_TRUE(snapshot_writer.Finalize());
- auto cow_reader = snapshot_writer.OpenReader();
- ASSERT_NE(cow_reader, nullptr);
- ASSERT_TRUE(snapshot_writer.AddRawBlocks(1, buffer.data(), buffer.size()));
- ASSERT_TRUE(snapshot_writer.AddRawBlocks(2, buffer.data(), buffer.size()));
- ASSERT_TRUE(snapshot_writer.Finalize());
- // After wrigin some data, if we call OpenReader() again, writes should
- // be visible to the newly opened reader. update_engine relies on this
- // behavior for verity writes.
- cow_reader = snapshot_writer.OpenReader();
- ASSERT_NE(cow_reader, nullptr);
- std::vector<unsigned char> read_back;
- read_back.resize(buffer.size());
- cow_reader->Seek(BLOCK_SIZE, SEEK_SET);
- const auto bytes_read = cow_reader->Read(read_back.data(), read_back.size());
- ASSERT_EQ((size_t)(bytes_read), BLOCK_SIZE);
- ASSERT_EQ(read_back, buffer);
-}
-
-} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 5eb200378..a44de8419 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -48,17 +48,6 @@ bool DumpCmdHandler(int /*argc*/, char** argv) {
return SnapshotManager::New()->Dump(std::cout);
}
-bool MapCmdHandler(int, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
- using namespace std::chrono_literals;
- return SnapshotManager::New()->MapAllSnapshots(5000ms);
-}
-
-bool UnmapCmdHandler(int, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
- return SnapshotManager::New()->UnmapAllSnapshots();
-}
-
bool MergeCmdHandler(int /*argc*/, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
@@ -69,8 +58,6 @@ static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
// clang-format off
{"dump", DumpCmdHandler},
{"merge", MergeCmdHandler},
- {"map", MapCmdHandler},
- {"unmap", UnmapCmdHandler},
// clang-format on
};
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
deleted file mode 100644
index 57a61a791..000000000
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ /dev/null
@@ -1,769 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd.h"
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#include <libsnapshot/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-Snapuserd::Snapuserd(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device) {
- misc_name_ = misc_name;
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = "/dev/dm-user/" + misc_name;
-}
-
-bool Snapuserd::InitializeWorkers() {
- for (int i = 0; i < NUM_THREADS_PER_PARTITION; i++) {
- std::unique_ptr<WorkerThread> wt = std::make_unique<WorkerThread>(
- cow_device_, backing_store_device_, control_device_, misc_name_, GetSharedPtr());
-
- worker_threads_.push_back(std::move(wt));
- }
-
- read_ahead_thread_ = std::make_unique<ReadAheadThread>(cow_device_, backing_store_device_,
- misc_name_, GetSharedPtr());
- return true;
-}
-
-bool Snapuserd::CommitMerge(int num_merge_ops) {
- struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
- ch->num_merge_ops += num_merge_ops;
-
- if (read_ahead_feature_ && read_ahead_ops_.size() > 0) {
- struct BufferState* ra_state = GetBufferState();
- ra_state->read_ahead_state = kCowReadAheadInProgress;
- }
-
- int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync header failed: " << ret;
- return false;
- }
-
- merge_initiated_ = true;
-
- return true;
-}
-
-void Snapuserd::PrepareReadAhead() {
- if (!read_ahead_feature_) {
- return;
- }
-
- struct BufferState* ra_state = GetBufferState();
- // Check if the data has to be re-constructed from COW device
- if (ra_state->read_ahead_state == kCowReadAheadDone) {
- populate_data_from_cow_ = true;
- } else {
- populate_data_from_cow_ = false;
- }
-
- StartReadAhead();
-}
-
-bool Snapuserd::GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer) {
- if (!lock->owns_lock()) {
- SNAP_LOG(ERROR) << "GetRABuffer - Lock not held";
- return false;
- }
- std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
-
- // This will be true only for IO's generated as part of reading a root
- // filesystem. IO's related to merge should always be in read-ahead cache.
- if (it == read_ahead_buffer_map_.end()) {
- return false;
- }
-
- // Theoretically, we can send the data back from the read-ahead buffer
- // all the way to the kernel without memcpy. However, if the IO is
- // un-aligned, the wrapper function will need to touch the read-ahead
- // buffers and transitions will be bit more complicated.
- memcpy(buffer, it->second, BLOCK_SZ);
- return true;
-}
-
-// ========== State transition functions for read-ahead operations ===========
-
-bool Snapuserd::GetReadAheadPopulatedBuffer(uint64_t block, void* buffer) {
- if (!read_ahead_feature_) {
- return false;
- }
-
- {
- std::unique_lock<std::mutex> lock(lock_);
- if (io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE) {
- return false;
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS) {
- return GetRABuffer(&lock, block, buffer);
- }
- }
-
- {
- // Read-ahead thread IO is in-progress. Wait for it to complete
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS)) {
- cv.wait(lock);
- }
-
- return GetRABuffer(&lock, block, buffer);
- }
-}
-
-// This is invoked by read-ahead thread waiting for merge IO's
-// to complete
-bool Snapuserd::WaitForMergeToComplete() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_BEGIN ||
- io_state_ == READ_AHEAD_IO_TRANSITION::IO_TERMINATED)) {
- cv.wait(lock);
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
-
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_IN_PROGRESS;
- return true;
- }
-}
-
-// This is invoked during the launch of worker threads. We wait
-// for read-ahead thread to by fully up before worker threads
-// are launched; else we will have a race between worker threads
-// and read-ahead thread specifically during re-construction.
-bool Snapuserd::WaitForReadAheadToStart() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS ||
- io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE)) {
- cv.wait(lock);
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE) {
- return false;
- }
-
- return true;
- }
-}
-
-// Invoked by worker threads when a sequence of merge operation
-// is complete notifying read-ahead thread to make forward
-// progress.
-void Snapuserd::StartReadAhead() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_BEGIN;
- }
-
- cv.notify_one();
-}
-
-void Snapuserd::MergeCompleted() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::IO_TERMINATED;
- }
-
- cv.notify_one();
-}
-
-bool Snapuserd::ReadAheadIOCompleted(bool sync) {
- if (sync) {
- // Flush the entire buffer region
- int ret = msync(mapped_addr_, total_mapped_addr_length_, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync failed after ReadAheadIOCompleted: " << ret;
- return false;
- }
-
- // Metadata and data are synced. Now, update the state.
- // We need to update the state after flushing data; if there is a crash
- // when read-ahead IO is in progress, the state of data in the COW file
- // is unknown. kCowReadAheadDone acts as a checkpoint wherein the data
- // in the scratch space is good and during next reboot, read-ahead thread
- // can safely re-construct the data.
- struct BufferState* ra_state = GetBufferState();
- ra_state->read_ahead_state = kCowReadAheadDone;
-
- ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync failed to flush Readahead completion state...";
- return false;
- }
- }
-
- // Notify the worker threads
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS;
- }
-
- cv.notify_all();
- return true;
-}
-
-void Snapuserd::ReadAheadIOFailed() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE;
- }
-
- cv.notify_all();
-}
-
-//========== End of state transition functions ====================
-
-bool Snapuserd::IsChunkIdMetadata(chunk_t chunk) {
- uint32_t stride = exceptions_per_area_ + 1;
- lldiv_t divresult = lldiv(chunk, stride);
-
- return (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS);
-}
-
-// Find the next free chunk-id to be assigned. Check if the next free
-// chunk-id represents a metadata page. If so, skip it.
-chunk_t Snapuserd::GetNextAllocatableChunkId(chunk_t chunk) {
- chunk_t next_chunk = chunk + 1;
-
- if (IsChunkIdMetadata(next_chunk)) {
- next_chunk += 1;
- }
- return next_chunk;
-}
-
-void Snapuserd::CheckMergeCompletionStatus() {
- if (!merge_initiated_) {
- SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->total_data_ops();
- return;
- }
-
- struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-
- SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
- << " Total-data-ops: " << reader_->total_data_ops();
-}
-
-/*
- * Read the metadata from COW device and
- * construct the metadata as required by the kernel.
- *
- * Please see design on kernel COW format
- *
- * 1: Read the metadata from internal COW device
- * 2: There are 3 COW operations:
- * a: Replace op
- * b: Copy op
- * c: Zero op
- * 3: For each of the 3 operations, op->new_block
- * represents the block number in the base device
- * for which one of the 3 operations have to be applied.
- * This represents the old_chunk in the kernel COW format
- * 4: We need to assign new_chunk for a corresponding old_chunk
- * 5: The algorithm is similar to how kernel assigns chunk number
- * while creating exceptions. However, there are few cases
- * which needs to be addressed here:
- * a: During merge process, kernel scans the metadata page
- * from backwards when merge is initiated. Since, we need
- * to make sure that the merge ordering follows our COW format,
- * we read the COW operation from backwards and populate the
- * metadata so that when kernel starts the merging from backwards,
- * those ops correspond to the beginning of our COW format.
- * b: Kernel can merge successive operations if the two chunk IDs
- * are contiguous. This can be problematic when there is a crash
- * during merge; specifically when the merge operation has dependency.
- * These dependencies can only happen during copy operations.
- *
- * To avoid this problem, we make sure overlap copy operations
- * are not batch merged.
- * 6: Use a monotonically increasing chunk number to assign the
- * new_chunk
- * 7: Each chunk-id represents either
- * a: Metadata page or
- * b: Data page
- * 8: Chunk-id representing a data page is stored in a map.
- * 9: Chunk-id representing a metadata page is converted into a vector
- * index. We store this in vector as kernel requests metadata during
- * two stage:
- * a: When initial dm-snapshot device is created, kernel requests
- * all the metadata and stores it in its internal data-structures.
- * b: During merge, kernel once again requests the same metadata
- * once-again.
- * In both these cases, a quick lookup based on chunk-id is done.
- * 10: When chunk number is incremented, we need to make sure that
- * if the chunk is representing a metadata page and skip.
- * 11: Each 4k page will contain 256 disk exceptions. We call this
- * exceptions_per_area_
- * 12: Kernel will stop issuing metadata IO request when new-chunk ID is 0.
- */
-bool Snapuserd::ReadMetadata() {
- reader_ = std::make_unique<CowReader>();
- CowHeader header;
- CowOptions options;
- bool metadata_found = false;
- int replace_ops = 0, zero_ops = 0, copy_ops = 0;
-
- SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
-
- if (!reader_->Parse(cow_fd_)) {
- SNAP_LOG(ERROR) << "Failed to parse";
- return false;
- }
-
- if (!reader_->GetHeader(&header)) {
- SNAP_LOG(ERROR) << "Failed to get header";
- return false;
- }
-
- if (!(header.block_size == BLOCK_SZ)) {
- SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
- return false;
- }
-
- reader_->InitializeMerge();
- SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
-
- if (!MmapMetadata()) {
- SNAP_LOG(ERROR) << "mmap failed";
- return false;
- }
-
- // Initialize the iterator for reading metadata
- cowop_riter_ = reader_->GetRevOpIter();
-
- exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
-
- // Start from chunk number 2. Chunk 0 represents header and chunk 1
- // represents first metadata page.
- chunk_t data_chunk_id = NUM_SNAPSHOT_HDR_CHUNKS + 1;
- size_t num_ops = 0;
-
- loff_t offset = 0;
- std::unique_ptr<uint8_t[]> de_ptr =
- std::make_unique<uint8_t[]>(exceptions_per_area_ * sizeof(struct disk_exception));
-
- // This memset is important. Kernel will stop issuing IO when new-chunk ID
- // is 0. When Area is not filled completely with all 256 exceptions,
- // this memset will ensure that metadata read is completed.
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- while (!cowop_riter_->Done()) {
- const CowOperation* cow_op = &cowop_riter_->Get();
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
-
- if (IsMetadataOp(*cow_op)) {
- cowop_riter_->Next();
- continue;
- }
-
- metadata_found = true;
- // This loop will handle all the replace and zero ops.
- // We will handle the copy ops later as it requires special
- // handling of assigning chunk-id's. Furthermore, we make
- // sure that replace/zero and copy ops are not batch merged; hence,
- // the bump in the chunk_id before break of this loop
- if (cow_op->type == kCowCopyOp) {
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- break;
- }
-
- if (cow_op->type == kCowReplaceOp) {
- replace_ops++;
- } else if (cow_op->type == kCowZeroOp) {
- zero_ops++;
- }
-
- // Construct the disk-exception
- de->old_chunk = cow_op->new_block;
- de->new_chunk = data_chunk_id;
-
-
- // Store operation pointer.
- chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
- num_ops += 1;
- offset += sizeof(struct disk_exception);
- cowop_riter_->Next();
-
- SNAP_LOG(DEBUG) << num_ops << ":"
- << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
-
- if (num_ops == exceptions_per_area_) {
- // Store it in vector at the right index. This maps the chunk-id to
- // vector index.
- vec_.push_back(std::move(de_ptr));
- offset = 0;
- num_ops = 0;
-
- // Create buffer for next area
- de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
- sizeof(struct disk_exception));
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- if (cowop_riter_->Done()) {
- vec_.push_back(std::move(de_ptr));
- }
- }
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- }
-
- int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
- std::optional<chunk_t> prev_id = {};
- std::vector<const CowOperation*> vec;
- std::set<uint64_t> dest_blocks;
- std::set<uint64_t> source_blocks;
- size_t pending_copy_ops = exceptions_per_area_ - num_ops;
- uint64_t total_copy_ops = reader_->total_copy_ops();
-
- SNAP_LOG(DEBUG) << " Processing copy-ops at Area: " << vec_.size()
- << " Number of replace/zero ops completed in this area: " << num_ops
- << " Pending copy ops for this area: " << pending_copy_ops;
- while (!cowop_riter_->Done()) {
- do {
- const CowOperation* cow_op = &cowop_riter_->Get();
- if (IsMetadataOp(*cow_op)) {
- cowop_riter_->Next();
- continue;
- }
-
- // We have two cases specific cases:
- //
- // =====================================================
- // Case 1: Overlapping copy regions
- //
- // Ex:
- //
- // Source -> Destination
- //
- // 1: 15 -> 18
- // 2: 16 -> 19
- // 3: 17 -> 20
- // 4: 18 -> 21
- // 5: 19 -> 22
- // 6: 20 -> 23
- //
- // We have 6 copy operations to be executed in OTA and there is a overlap. Update-engine
- // will write to COW file as follows:
- //
- // Op-1: 20 -> 23
- // Op-2: 19 -> 22
- // Op-3: 18 -> 21
- // Op-4: 17 -> 20
- // Op-5: 16 -> 19
- // Op-6: 15 -> 18
- //
- // Note that the blocks numbers are contiguous. Hence, all 6 copy
- // operations can be batch merged. However, that will be
- // problematic if we have a crash as block 20, 19, 18 would have
- // been overwritten and hence subsequent recovery may end up with
- // a silent data corruption when op-1, op-2 and op-3 are
- // re-executed.
- //
- // To address the above problem, read-ahead thread will
- // read all the 6 source blocks, cache them in the scratch
- // space of the COW file. During merge, read-ahead
- // thread will serve the blocks from the read-ahead cache.
- // If there is a crash during merge; on subsequent reboot,
- // read-ahead thread will recover the data from the
- // scratch space and re-construct it thereby there
- // is no loss of data.
- //
- // Note that we will follow the same order of COW operations
- // as present in the COW file. This will make sure that\
- // the merge of operations are done based on the ops present
- // in the file.
- //===========================================================
- if (prev_id.has_value()) {
- if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
- break;
- }
- }
- metadata_found = true;
- pending_copy_ops -= 1;
- vec.push_back(cow_op);
- dest_blocks.insert(cow_op->source);
- source_blocks.insert(cow_op->new_block);
- prev_id = cow_op->new_block;
- cowop_riter_->Next();
- } while (!cowop_riter_->Done() && pending_copy_ops);
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
- << " Area: " << vec_.size() << " Area offset: " << offset
- << " Pending-copy-ops in this area: " << pending_copy_ops;
-
- for (size_t i = 0; i < vec.size(); i++) {
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- const CowOperation* cow_op = vec[i];
-
- de->old_chunk = cow_op->new_block;
- de->new_chunk = data_chunk_id;
-
- // Store operation pointer.
- chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
- offset += sizeof(struct disk_exception);
- num_ops += 1;
- copy_ops++;
- if (read_ahead_feature_) {
- read_ahead_ops_.push_back(cow_op);
- }
-
- SNAP_LOG(DEBUG) << num_ops << ":"
- << " Copy-op: "
- << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
-
- if (num_ops == exceptions_per_area_) {
- // Store it in vector at the right index. This maps the chunk-id to
- // vector index.
- vec_.push_back(std::move(de_ptr));
- num_ops = 0;
- offset = 0;
-
- // Create buffer for next area
- de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
- sizeof(struct disk_exception));
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- if (cowop_riter_->Done()) {
- vec_.push_back(std::move(de_ptr));
- SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
- }
-
- if (!(pending_copy_ops == 0)) {
- SNAP_LOG(ERROR)
- << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
- return false;
- }
- pending_copy_ops = exceptions_per_area_;
- }
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- total_copy_ops -= 1;
- /*
- * Split the number of ops based on the size of read-ahead buffer
- * region. We need to ensure that kernel doesn't issue IO on blocks
- * which are not read by the read-ahead thread.
- */
- if (read_ahead_feature_ && (total_copy_ops % num_ra_ops_per_iter == 0)) {
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- }
- }
- vec.clear();
- dest_blocks.clear();
- source_blocks.clear();
- prev_id.reset();
- }
-
- // Partially filled area or there is no metadata
- // If there is no metadata, fill with zero so that kernel
- // is aware that merge is completed.
- if (num_ops || !metadata_found) {
- vec_.push_back(std::move(de_ptr));
- SNAP_LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
- << "Areas : " << vec_.size();
- }
-
- chunk_vec_.shrink_to_fit();
- vec_.shrink_to_fit();
- read_ahead_ops_.shrink_to_fit();
-
- // Sort the vector based on sectors as we need this during un-aligned access
- std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
-
- SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
- << " Num Sector: " << ChunkToSector(data_chunk_id)
- << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
- << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
- << " Num-ops-merged: " << header.num_merge_ops
- << " Total-data-ops: " << reader_->total_data_ops();
-
- // Total number of sectors required for creating dm-user device
- num_sectors_ = ChunkToSector(data_chunk_id);
- merge_initiated_ = false;
- PrepareReadAhead();
-
- return true;
-}
-
-bool Snapuserd::MmapMetadata() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- if (header.major_version >= 2 && header.buffer_size > 0) {
- total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
- read_ahead_feature_ = true;
- } else {
- // mmap the first 4k page - older COW format
- total_mapped_addr_length_ = BLOCK_SZ;
- read_ahead_feature_ = false;
- }
-
- mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE, MAP_SHARED,
- cow_fd_.get(), 0);
- if (mapped_addr_ == MAP_FAILED) {
- SNAP_LOG(ERROR) << "mmap metadata failed";
- return false;
- }
-
- return true;
-}
-
-void Snapuserd::UnmapBufferRegion() {
- int ret = munmap(mapped_addr_, total_mapped_addr_length_);
- if (ret < 0) {
- SNAP_PLOG(ERROR) << "munmap failed";
- }
-}
-
-void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
- unsigned int, const char* message) {
- if (severity == android::base::ERROR) {
- fprintf(stderr, "%s\n", message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-bool Snapuserd::InitCowDevice() {
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- return ReadMetadata();
-}
-
-/*
- * Entry point to launch threads
- */
-bool Snapuserd::Start() {
- std::vector<std::future<bool>> threads;
- std::future<bool> ra_thread;
- bool rathread = (read_ahead_feature_ && (read_ahead_ops_.size() > 0));
-
- // Start the read-ahead thread and wait
- // for it as the data has to be re-constructed
- // from COW device.
- if (rathread) {
- ra_thread = std::async(std::launch::async, &ReadAheadThread::RunThread,
- read_ahead_thread_.get());
- if (!WaitForReadAheadToStart()) {
- SNAP_LOG(ERROR) << "Failed to start Read-ahead thread...";
- return false;
- }
-
- SNAP_LOG(INFO) << "Read-ahead thread started...";
- }
-
- // Launch worker threads
- for (int i = 0; i < worker_threads_.size(); i++) {
- threads.emplace_back(
- std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get()));
- }
-
- bool ret = true;
- for (auto& t : threads) {
- ret = t.get() && ret;
- }
-
- if (rathread) {
- // Notify the read-ahead thread that all worker threads
- // are done. We need this explicit notification when
- // there is an IO failure or there was a switch
- // of dm-user table; thus, forcing the read-ahead
- // thread to wake up.
- MergeCompleted();
- ret = ret && ra_thread.get();
- }
-
- return ret;
-}
-
-uint64_t Snapuserd::GetBufferMetadataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- size_t size = header.header_size + sizeof(BufferState);
- return size;
-}
-
-/*
- * Metadata for read-ahead is 16 bytes. For a 2 MB region, we will
- * end up with 8k (2 PAGE) worth of metadata. Thus, a 2MB buffer
- * region is split into:
- *
- * 1: 8k metadata
- *
- */
-size_t Snapuserd::GetBufferMetadataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- size_t metadata_bytes = (header.buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ;
- return metadata_bytes;
-}
-
-size_t Snapuserd::GetBufferDataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- return (header.header_size + GetBufferMetadataSize());
-}
-
-/*
- * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
- */
-size_t Snapuserd::GetBufferDataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- size_t size = header.buffer_size - GetBufferMetadataSize();
- return size;
-}
-
-struct BufferState* Snapuserd::GetBufferState() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
- return ra_state;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
deleted file mode 100644
index 212c78e39..000000000
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <linux/types.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-
-#include <bitset>
-#include <condition_variable>
-#include <csignal>
-#include <cstring>
-#include <future>
-#include <iostream>
-#include <limits>
-#include <map>
-#include <mutex>
-#include <string>
-#include <thread>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_kernel.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using namespace std::chrono_literals;
-
-static constexpr size_t PAYLOAD_SIZE = (1UL << 20);
-static_assert(PAYLOAD_SIZE >= BLOCK_SZ);
-
-/*
- * With 4 threads, we get optimal performance
- * when update_verifier reads the partition during
- * boot.
- */
-static constexpr int NUM_THREADS_PER_PARTITION = 4;
-
-/*
- * State transitions between worker threads and read-ahead
- * threads.
- *
- * READ_AHEAD_BEGIN: Worker threads initiates the read-ahead
- * thread to begin reading the copy operations
- * for each bounded region.
- *
- * READ_AHEAD_IN_PROGRESS: When read ahead thread is in-flight
- * and reading the copy operations.
- *
- * IO_IN_PROGRESS: Merge operation is in-progress by worker threads.
- *
- * IO_TERMINATED: When all the worker threads are done, request the
- * read-ahead thread to terminate
- *
- * READ_AHEAD_FAILURE: If there are any IO failures when read-ahead
- * thread is reading from COW device.
- *
- * The transition of each states is described in snapuserd_readahead.cpp
- */
-enum class READ_AHEAD_IO_TRANSITION {
- READ_AHEAD_BEGIN,
- READ_AHEAD_IN_PROGRESS,
- IO_IN_PROGRESS,
- IO_TERMINATED,
- READ_AHEAD_FAILURE,
-};
-
-class BufferSink : public IByteSink {
- public:
- void Initialize(size_t size);
- void* GetBufPtr() { return buffer_.get(); }
- void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
- void* GetPayloadBuffer(size_t size);
- void* GetBuffer(size_t requested, size_t* actual) override;
- void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
- struct dm_user_header* GetHeaderPtr();
- bool ReturnData(void*, size_t) override { return true; }
- void ResetBufferOffset() { buffer_offset_ = 0; }
- void* GetPayloadBufPtr();
-
- private:
- std::unique_ptr<uint8_t[]> buffer_;
- loff_t buffer_offset_;
- size_t buffer_size_;
-};
-
-class Snapuserd;
-
-class ReadAheadThread {
- public:
- ReadAheadThread(const std::string& cow_device, const std::string& backing_device,
- const std::string& misc_name, std::shared_ptr<Snapuserd> snapuserd);
- bool RunThread();
-
- private:
- void InitializeIter();
- bool IterDone();
- void IterNext();
- const CowOperation* GetIterOp();
- void InitializeBuffer();
-
- bool InitializeFds();
- void CloseFds() {
- cow_fd_ = {};
- backing_store_fd_ = {};
- }
-
- bool ReadAheadIOStart();
- void PrepareReadAhead(uint64_t* source_block, int* pending_ops, std::vector<uint64_t>& blocks);
- bool ReconstructDataFromCow();
- void CheckOverlap(const CowOperation* cow_op);
-
- void* read_ahead_buffer_;
- void* metadata_buffer_;
- std::vector<const CowOperation*>::reverse_iterator read_ahead_iter_;
- std::string cow_device_;
- std::string backing_store_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
-
- std::shared_ptr<Snapuserd> snapuserd_;
-
- std::unordered_set<uint64_t> dest_blocks_;
- std::unordered_set<uint64_t> source_blocks_;
- bool overlap_;
-};
-
-class WorkerThread {
- public:
- WorkerThread(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- std::shared_ptr<Snapuserd> snapuserd);
- bool RunThread();
-
- private:
- // Initialization
- void InitializeBufsink();
- bool InitializeFds();
- bool InitReader();
- void CloseFds() {
- ctrl_fd_ = {};
- backing_store_fd_ = {};
- }
-
- // Functions interacting with dm-user
- bool ReadDmUserHeader();
- bool DmuserReadRequest();
- bool DmuserWriteRequest();
- bool ReadDmUserPayload(void* buffer, size_t size);
- bool WriteDmUserPayload(size_t size, bool header_response);
-
- bool ReadDiskExceptions(chunk_t chunk, size_t size);
- bool ZerofillDiskExceptions(size_t read_size);
- void ConstructKernelCowHeader();
-
- // IO Path
- bool ProcessIORequest();
- int ReadData(sector_t sector, size_t size);
- int ReadUnalignedSector(sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
-
- // Processing COW operations
- bool ProcessCowOp(const CowOperation* cow_op);
- bool ProcessReplaceOp(const CowOperation* cow_op);
- bool ProcessCopyOp(const CowOperation* cow_op);
- bool ProcessZeroOp();
-
- bool ReadFromBaseDevice(const CowOperation* cow_op);
- bool GetReadAheadPopulatedBuffer(const CowOperation* cow_op);
-
- // Merge related functions
- bool ProcessMergeComplete(chunk_t chunk, void* buffer);
- loff_t GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
- int* unmerged_exceptions);
-
- int GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions, bool* copy_op, bool* commit);
-
- sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
- chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-
- std::unique_ptr<CowReader> reader_;
- BufferSink bufsink_;
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string control_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
- unique_fd ctrl_fd_;
-
- std::shared_ptr<Snapuserd> snapuserd_;
- uint32_t exceptions_per_area_;
-};
-
-class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
- public:
- Snapuserd(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device);
- bool InitCowDevice();
- bool Start();
- const std::string& GetControlDevicePath() { return control_device_; }
- const std::string& GetMiscName() { return misc_name_; }
- uint64_t GetNumSectors() { return num_sectors_; }
- bool IsAttached() const { return attached_; }
- void AttachControlDevice() { attached_ = true; }
-
- void CheckMergeCompletionStatus();
- bool CommitMerge(int num_merge_ops);
-
- void CloseFds() { cow_fd_ = {}; }
- void FreeResources() {
- worker_threads_.clear();
- read_ahead_thread_ = nullptr;
- }
- size_t GetMetadataAreaSize() { return vec_.size(); }
- void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
-
- bool InitializeWorkers();
- std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
-
- std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
- const std::vector<std::unique_ptr<uint8_t[]>>& GetMetadataVec() const { return vec_; }
-
- static bool compare(std::pair<sector_t, const CowOperation*> p1,
- std::pair<sector_t, const CowOperation*> p2) {
- return p1.first < p2.first;
- }
-
- void UnmapBufferRegion();
- bool MmapMetadata();
-
- // Read-ahead related functions
- std::vector<const CowOperation*>& GetReadAheadOpsVec() { return read_ahead_ops_; }
- std::unordered_map<uint64_t, void*>& GetReadAheadMap() { return read_ahead_buffer_map_; }
- void* GetMappedAddr() { return mapped_addr_; }
- bool IsReadAheadFeaturePresent() { return read_ahead_feature_; }
- void PrepareReadAhead();
- void StartReadAhead();
- void MergeCompleted();
- bool ReadAheadIOCompleted(bool sync);
- void ReadAheadIOFailed();
- bool WaitForMergeToComplete();
- bool GetReadAheadPopulatedBuffer(uint64_t block, void* buffer);
- bool ReconstructDataFromCow() { return populate_data_from_cow_; }
- void ReconstructDataFromCowFinish() { populate_data_from_cow_ = false; }
- bool WaitForReadAheadToStart();
-
- uint64_t GetBufferMetadataOffset();
- size_t GetBufferMetadataSize();
- size_t GetBufferDataOffset();
- size_t GetBufferDataSize();
-
- // Final block to be merged in a given read-ahead buffer region
- void SetFinalBlockMerged(uint64_t x) { final_block_merged_ = x; }
- uint64_t GetFinalBlockMerged() { return final_block_merged_; }
- // Total number of blocks to be merged in a given read-ahead buffer region
- void SetTotalRaBlocksMerged(int x) { total_ra_blocks_merged_ = x; }
- int GetTotalRaBlocksMerged() { return total_ra_blocks_merged_; }
-
- private:
- bool IsChunkIdMetadata(chunk_t chunk);
- chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
-
- bool GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer);
- bool ReadMetadata();
- sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
- chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
- bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
- struct BufferState* GetBufferState();
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string control_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
-
- uint32_t exceptions_per_area_;
- uint64_t num_sectors_;
-
- std::unique_ptr<ICowOpIter> cowop_iter_;
- std::unique_ptr<ICowOpReverseIter> cowop_riter_;
- std::unique_ptr<CowReader> reader_;
-
- // Vector of disk exception which is a
- // mapping of old-chunk to new-chunk
- std::vector<std::unique_ptr<uint8_t[]>> vec_;
-
- // chunk_vec stores the pseudo mapping of sector
- // to COW operations.
- std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;
-
- std::mutex lock_;
- std::condition_variable cv;
-
- void* mapped_addr_;
- size_t total_mapped_addr_length_;
-
- std::vector<std::unique_ptr<WorkerThread>> worker_threads_;
- // Read-ahead related
- std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;
- std::vector<const CowOperation*> read_ahead_ops_;
- bool populate_data_from_cow_ = false;
- bool read_ahead_feature_;
- uint64_t final_block_merged_;
- int total_ra_blocks_merged_ = 0;
- READ_AHEAD_IO_TRANSITION io_state_;
- std::unique_ptr<ReadAheadThread> read_ahead_thread_;
-
- bool merge_initiated_ = false;
- bool attached_ = false;
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd.rc
deleted file mode 100644
index 4bf34a2bd..000000000
--- a/fs_mgr/libsnapshot/snapuserd.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service snapuserd /system/bin/snapuserd
- socket snapuserd stream 0660 system system
- oneshot
- disabled
- user root
- group root system
- seclabel u:r:snapuserd:s0
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
deleted file mode 100644
index 41ab34409..000000000
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <arpa/inet.h>
-#include <cutils/sockets.h>
-#include <errno.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <sstream>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <libsnapshot/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace std::chrono_literals;
-using android::base::unique_fd;
-
-bool EnsureSnapuserdStarted() {
- if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
- return true;
- }
-
- android::base::SetProperty("ctl.start", "snapuserd");
- if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
- LOG(ERROR) << "Timed out waiting for snapuserd to start.";
- return false;
- }
- return true;
-}
-
-SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
-
-static inline bool IsRetryErrno() {
- return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;
-}
-
-std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
- std::chrono::milliseconds timeout_ms) {
- unique_fd fd;
- auto start = std::chrono::steady_clock::now();
- while (true) {
- fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM));
- if (fd >= 0) break;
- if (fd < 0 && !IsRetryErrno()) {
- PLOG(ERROR) << "connect failed: " << socket_name;
- return nullptr;
- }
-
- auto now = std::chrono::steady_clock::now();
- auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
- if (elapsed >= timeout_ms) {
- LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
- return nullptr;
- }
-
- std::this_thread::sleep_for(100ms);
- }
-
- auto client = std::make_unique<SnapuserdClient>(std::move(fd));
- if (!client->ValidateConnection()) {
- return nullptr;
- }
- return client;
-}
-
-bool SnapuserdClient::ValidateConnection() {
- if (!Sendmsg("query")) {
- return false;
- }
-
- std::string str = Receivemsg();
-
- // If the daemon is passive then fallback to secondary active daemon. Daemon
- // is passive during transition phase.
- if (str.find("passive") != std::string::npos) {
- LOG(ERROR) << "Snapuserd is terminating";
- return false;
- }
-
- if (str != "active") {
- LOG(ERROR) << "Received failure querying daemon";
- return false;
- }
- return true;
-}
-
-bool SnapuserdClient::Sendmsg(const std::string& msg) {
- LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_;
- ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), MSG_NOSIGNAL));
- if (numBytesSent < 0) {
- PLOG(ERROR) << "Send failed";
- return false;
- }
-
- if ((size_t)numBytesSent < msg.size()) {
- LOG(ERROR) << "Partial data sent, expected " << msg.size() << " bytes, sent "
- << numBytesSent;
- return false;
- }
- return true;
-}
-
-bool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) {
- std::string msg = "delete," + control_device;
- if (!Sendmsg(msg)) {
- LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
- return false;
- }
- std::string response = Receivemsg();
- if (response != "success") {
- LOG(ERROR) << "Failed waiting to delete device " << control_device;
- return false;
- }
- return true;
-}
-
-std::string SnapuserdClient::Receivemsg() {
- char msg[PACKET_SIZE];
- ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
- if (ret < 0) {
- PLOG(ERROR) << "Snapuserd:client: recv failed";
- return {};
- }
- if (ret == 0) {
- LOG(DEBUG) << "Snapuserd:client disconnected";
- return {};
- }
- return std::string(msg, ret);
-}
-
-bool SnapuserdClient::StopSnapuserd() {
- if (!Sendmsg("stop")) {
- LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
- return false;
- }
-
- sockfd_ = {};
- return true;
-}
-
-bool SnapuserdClient::AttachDmUser(const std::string& misc_name) {
- std::string msg = "start," + misc_name;
- if (!Sendmsg(msg)) {
- LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
- return false;
- }
-
- std::string str = Receivemsg();
- if (str != "success") {
- LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
- return false;
- }
-
- LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
- return true;
-}
-
-uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device) {
- std::vector<std::string> parts = {"init", misc_name, cow_device, backing_device};
- std::string msg = android::base::Join(parts, ",");
- if (!Sendmsg(msg)) {
- LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
- return 0;
- }
-
- std::string str = Receivemsg();
-
- std::vector<std::string> input = android::base::Split(str, ",");
-
- if (input.empty() || input[0] != "success") {
- LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon";
- return 0;
- }
-
- LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device
- << " Num-sectors: " << input[1];
-
- uint64_t num_sectors = 0;
- if (!android::base::ParseUint(input[1], &num_sectors)) {
- LOG(ERROR) << "Failed to parse input string to sectors";
- return 0;
- }
- return num_sectors;
-}
-
-bool SnapuserdClient::DetachSnapuserd() {
- if (!Sendmsg("detach")) {
- LOG(ERROR) << "Failed to detach snapuserd.";
- return false;
- }
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
deleted file mode 100644
index 7fa01b78f..000000000
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd_daemon.h"
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <gflags/gflags.h>
-#include <libsnapshot/snapuserd_client.h>
-
-#include "snapuserd_server.h"
-
-DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
-DEFINE_bool(no_socket, false,
- "If true, no socket is used. Each additional argument is an INIT message.");
-
-namespace android {
-namespace snapshot {
-
-bool Daemon::StartServer(int argc, char** argv) {
- int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
-
- if (!FLAGS_no_socket) {
- return server_.Start(FLAGS_socket);
- }
-
- for (int i = arg_start; i < argc; i++) {
- auto parts = android::base::Split(argv[i], ",");
- if (parts.size() != 3) {
- LOG(ERROR) << "Malformed message, expected three sub-arguments.";
- return false;
- }
- auto handler = server_.AddHandler(parts[0], parts[1], parts[2]);
- if (!handler || !server_.StartHandler(handler)) {
- return false;
- }
- }
-
- // Skip the accept() call to avoid spurious log spam. The server will still
- // run until all handlers have completed.
- server_.SetTerminating();
- return true;
-}
-
-void Daemon::MaskAllSignalsExceptIntAndTerm() {
- sigset_t signal_mask;
- sigfillset(&signal_mask);
- sigdelset(&signal_mask, SIGINT);
- sigdelset(&signal_mask, SIGTERM);
- sigdelset(&signal_mask, SIGPIPE);
- if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
- PLOG(ERROR) << "Failed to set sigprocmask";
- }
-}
-
-void Daemon::MaskAllSignals() {
- sigset_t signal_mask;
- sigfillset(&signal_mask);
- if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
- PLOG(ERROR) << "Couldn't mask all signals";
- }
-}
-
-void Daemon::Run() {
- sigfillset(&signal_mask_);
- sigdelset(&signal_mask_, SIGINT);
- sigdelset(&signal_mask_, SIGTERM);
-
- // Masking signals here ensure that after this point, we won't handle INT/TERM
- // until after we call into ppoll()
- signal(SIGINT, Daemon::SignalHandler);
- signal(SIGTERM, Daemon::SignalHandler);
- signal(SIGPIPE, Daemon::SignalHandler);
-
- LOG(DEBUG) << "Snapuserd-server: ready to accept connections";
-
- MaskAllSignalsExceptIntAndTerm();
-
- server_.Run();
-}
-
-void Daemon::Interrupt() {
- server_.Interrupt();
-}
-
-void Daemon::SignalHandler(int signal) {
- LOG(DEBUG) << "Snapuserd received signal: " << signal;
- switch (signal) {
- case SIGINT:
- case SIGTERM: {
- Daemon::Instance().Interrupt();
- break;
- }
- case SIGPIPE: {
- LOG(ERROR) << "Received SIGPIPE signal";
- break;
- }
- default:
- LOG(ERROR) << "Received unknown signal " << signal;
- break;
- }
-}
-
-} // namespace snapshot
-} // namespace android
-
-int main(int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
-
- android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
-
- if (!daemon.StartServer(argc, argv)) {
- LOG(ERROR) << "Snapuserd daemon failed to start.";
- exit(EXIT_FAILURE);
- }
- daemon.Run();
-
- return 0;
-}
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd_daemon.h
deleted file mode 100644
index f8afac559..000000000
--- a/fs_mgr/libsnapshot/snapuserd_daemon.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <poll.h>
-
-#include <string>
-#include <vector>
-
-#include "snapuserd_server.h"
-
-namespace android {
-namespace snapshot {
-
-class Daemon {
- // The Daemon class is a singleton to avoid
- // instantiating more than once
- public:
- Daemon() {}
-
- static Daemon& Instance() {
- static Daemon instance;
- return instance;
- }
-
- bool StartServer(int argc, char** argv);
- void Run();
- void Interrupt();
-
- private:
- // Signal mask used with ppoll()
- sigset_t signal_mask_;
-
- Daemon(Daemon const&) = delete;
- void operator=(Daemon const&) = delete;
-
- SnapuserdServer server_;
- void MaskAllSignalsExceptIntAndTerm();
- void MaskAllSignals();
- static void SignalHandler(int signal);
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd_readahead.cpp
deleted file mode 100644
index 16d5919a5..000000000
--- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd.h"
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#include <libsnapshot/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-/*
- * Merging a copy operation involves the following flow:
- *
- * 1: dm-snapshot layer requests merge for a 4k block. dm-user sends the request
- * to the daemon
- * 2: daemon reads the source block
- * 3: daemon copies the source data
- * 4: IO completion sent back to dm-user (a switch from user space to kernel)
- * 5: dm-snapshot merges the data to base device
- * 6: dm-snapshot sends the merge-completion IO to dm-user
- * 7: dm-user re-directs the merge completion IO to daemon (one more switch)
- * 8: daemon updates the COW file about the completed merge request (a write syscall) and followed
- * by a fysnc. 9: Send the IO completion back to dm-user
- *
- * The above sequence is a significant overhead especially when merging one 4k
- * block at a time.
- *
- * Read-ahead layer will optimize the above path by reading the data from base
- * device in the background so that merging thread can retrieve the data from
- * the read-ahead cache. Additionally, syncing of merged data is deferred to
- * read-ahead thread threadby the IO path is not bottlenecked.
- *
- * We create a scratch space of 2MB to store the read-ahead data in the COW
- * device.
- *
- * +-----------------------+
- * | Header (fixed) |
- * +-----------------------+
- * | Scratch space | <-- 2MB
- * +-----------------------+
- *
- * Scratch space is as follows:
- *
- * +-----------------------+
- * | Metadata | <- 4k page
- * +-----------------------+
- * | Metadata | <- 4k page
- * +-----------------------+
- * | |
- * | Read-ahead data |
- * | |
- * +-----------------------+
- *
- * State transitions and communication between read-ahead thread and worker
- * thread during merge:
- * =====================================================================
- *
- * Worker Threads Read-Ahead thread
- * ------------------------------------------------------------------
- *
- * |
- * |
- * --> -----------------READ_AHEAD_BEGIN------------->|
- * | | | READ_AHEAD_IN_PROGRESS
- * | WAIT |
- * | | |
- * | |<-----------------IO_IN_PROGRESS---------------
- * | | |
- * | | IO_IN_PRGRESS WAIT
- * | | |
- * |<--| |
- * | |
- * ------------------IO_TERMINATED--------------->|
- * END
- *
- *
- * ===================================================================
- *
- * Example:
- *
- * We have 6 copy operations to be executed in OTA and there is a overlap. Update-engine
- * will write to COW file as follows:
- *
- * Op-1: 20 -> 23
- * Op-2: 19 -> 22
- * Op-3: 18 -> 21
- * Op-4: 17 -> 20
- * Op-5: 16 -> 19
- * Op-6: 15 -> 18
- *
- * Read-ahead thread will read all the 6 source blocks and store the data in the
- * scratch space. Metadata will contain the destination block numbers. Thus,
- * scratch space will look something like this:
- *
- * +--------------+
- * | Block 23 |
- * | offset - 1 |
- * +--------------+
- * | Block 22 |
- * | offset - 2 |
- * +--------------+
- * | Block 21 |
- * | offset - 3 |
- * +--------------+
- * ...
- * ...
- * +--------------+
- * | Data-Block 20| <-- offset - 1
- * +--------------+
- * | Data-Block 19| <-- offset - 2
- * +--------------+
- * | Data-Block 18| <-- offset - 3
- * +--------------+
- * ...
- * ...
- *
- * ====================================================================
- * IO Path:
- *
- * Read-ahead will serve the data to worker threads during merge only
- * after metadata and data are persisted to the scratch space. Worker
- * threads during merge will always retrieve the data from cache; if the
- * cache is not populated, it will wait for the read-ahead thread to finish.
- * Furthermore, the number of operations merged will by synced to the header
- * only when all the blocks in the read-ahead cache are merged. In the above
- * case, when all 6 operations are merged, COW Header is updated with
- * num_merge_ops = 6.
- *
- * Merge resume after crash:
- *
- * Let's say we have a crash after 5 operations are merged. i.e. after
- * Op-5: 16->19 is completed but before the Op-6 is merged. Thus, COW Header
- * num_merge_ops will be 0 as the all the ops were not merged yet. During next
- * reboot, read-ahead thread will re-construct the data in-memory from the
- * scratch space; when merge resumes, Op-1 will be re-exectued. However,
- * data will be served from read-ahead cache safely even though, block 20
- * was over-written by Op-4.
- *
- */
-
-ReadAheadThread::ReadAheadThread(const std::string& cow_device, const std::string& backing_device,
- const std::string& misc_name,
- std::shared_ptr<Snapuserd> snapuserd) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- misc_name_ = misc_name;
- snapuserd_ = snapuserd;
-}
-
-void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
- if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(cow_op->source)) {
- overlap_ = true;
- }
-
- dest_blocks_.insert(cow_op->source);
- source_blocks_.insert(cow_op->new_block);
-}
-
-void ReadAheadThread::PrepareReadAhead(uint64_t* source_block, int* pending_ops,
- std::vector<uint64_t>& blocks) {
- int num_ops = *pending_ops;
- int nr_consecutive = 0;
-
- if (!IterDone() && num_ops) {
- // Get the first block
- const CowOperation* cow_op = GetIterOp();
- *source_block = cow_op->source;
- IterNext();
- num_ops -= 1;
- nr_consecutive = 1;
- blocks.push_back(cow_op->new_block);
-
- if (!overlap_) {
- CheckOverlap(cow_op);
- }
-
- /*
- * Find number of consecutive blocks working backwards.
- */
- while (!IterDone() && num_ops) {
- const CowOperation* op = GetIterOp();
- if (op->source != (*source_block - nr_consecutive)) {
- break;
- }
- nr_consecutive += 1;
- num_ops -= 1;
- blocks.push_back(op->new_block);
- IterNext();
-
- if (!overlap_) {
- CheckOverlap(op);
- }
- }
- }
-}
-
-bool ReadAheadThread::ReconstructDataFromCow() {
- std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- read_ahead_buffer_map.clear();
- loff_t metadata_offset = 0;
- loff_t start_data_offset = snapuserd_->GetBufferDataOffset();
- int num_ops = 0;
- int total_blocks_merged = 0;
-
- while (true) {
- struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
- (char*)metadata_buffer_ + metadata_offset);
-
- // Done reading metadata
- if (bm->new_block == 0 && bm->file_offset == 0) {
- break;
- }
-
- loff_t buffer_offset = bm->file_offset - start_data_offset;
- void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + buffer_offset);
- read_ahead_buffer_map[bm->new_block] = bufptr;
- num_ops += 1;
- total_blocks_merged += 1;
-
- metadata_offset += sizeof(struct ScratchMetadata);
- }
-
- // We are done re-constructing the mapping; however, we need to make sure
- // all the COW operations to-be merged are present in the re-constructed
- // mapping.
- while (!IterDone()) {
- const CowOperation* op = GetIterOp();
- if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {
- num_ops -= 1;
- snapuserd_->SetFinalBlockMerged(op->new_block);
- IterNext();
- } else {
- // Verify that we have covered all the ops which were re-constructed
- // from COW device - These are the ops which are being
- // re-constructed after crash.
- if (!(num_ops == 0)) {
- SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
- << " Pending ops: " << num_ops;
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
- break;
- }
- }
-
- snapuserd_->SetTotalRaBlocksMerged(total_blocks_merged);
-
- snapuserd_->ReconstructDataFromCowFinish();
-
- if (!snapuserd_->ReadAheadIOCompleted(true)) {
- SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
-
- SNAP_LOG(INFO) << "ReconstructDataFromCow success";
- return true;
-}
-
-bool ReadAheadThread::ReadAheadIOStart() {
- // Check if the data has to be constructed from the COW file.
- // This will be true only once during boot up after a crash
- // during merge.
- if (snapuserd_->ReconstructDataFromCow()) {
- return ReconstructDataFromCow();
- }
-
- std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- read_ahead_buffer_map.clear();
-
- int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;
- loff_t metadata_offset = 0;
-
- struct ScratchMetadata* bm =
- reinterpret_cast<struct ScratchMetadata*>((char*)metadata_buffer_ + metadata_offset);
-
- bm->new_block = 0;
- bm->file_offset = 0;
-
- std::vector<uint64_t> blocks;
-
- loff_t buffer_offset = 0;
- loff_t offset = 0;
- loff_t file_offset = snapuserd_->GetBufferDataOffset();
- int total_blocks_merged = 0;
- overlap_ = false;
- dest_blocks_.clear();
- source_blocks_.clear();
-
- while (true) {
- uint64_t source_block;
- int linear_blocks;
-
- PrepareReadAhead(&source_block, &num_ops, blocks);
- linear_blocks = blocks.size();
- if (linear_blocks == 0) {
- // No more blocks to read
- SNAP_LOG(DEBUG) << " Read-ahead completed....";
- break;
- }
-
- // Get the first block in the consecutive set of blocks
- source_block = source_block + 1 - linear_blocks;
- size_t io_size = (linear_blocks * BLOCK_SZ);
- num_ops -= linear_blocks;
- total_blocks_merged += linear_blocks;
-
- // Mark the block number as the one which will
- // be the final block to be merged in this entire region.
- // Read-ahead thread will get
- // notified when this block is merged to make
- // forward progress
- snapuserd_->SetFinalBlockMerged(blocks.back());
-
- while (linear_blocks) {
- uint64_t new_block = blocks.back();
- blocks.pop_back();
- // Assign the mapping
- void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + offset);
- read_ahead_buffer_map[new_block] = bufptr;
- offset += BLOCK_SZ;
-
- bm = reinterpret_cast<struct ScratchMetadata*>((char*)metadata_buffer_ +
- metadata_offset);
- bm->new_block = new_block;
- bm->file_offset = file_offset;
-
- metadata_offset += sizeof(struct ScratchMetadata);
- file_offset += BLOCK_SZ;
-
- linear_blocks -= 1;
- }
-
- // Read from the base device consecutive set of blocks in one shot
- if (!android::base::ReadFullyAtOffset(backing_store_fd_,
- (char*)read_ahead_buffer_ + buffer_offset, io_size,
- source_block * BLOCK_SZ)) {
- SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
- << "at block :" << source_block << " buffer_offset : " << buffer_offset
- << " io_size : " << io_size << " buf-addr : " << read_ahead_buffer_;
-
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
-
- // This is important - explicitly set the contents to zero. This is used
- // when re-constructing the data after crash. This indicates end of
- // reading metadata contents when re-constructing the data
- bm = reinterpret_cast<struct ScratchMetadata*>((char*)metadata_buffer_ + metadata_offset);
- bm->new_block = 0;
- bm->file_offset = 0;
-
- buffer_offset += io_size;
- }
-
- snapuserd_->SetTotalRaBlocksMerged(total_blocks_merged);
-
- // Flush the data only if we have a overlapping blocks in the region
- if (!snapuserd_->ReadAheadIOCompleted(overlap_)) {
- SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
- snapuserd_->ReadAheadIOFailed();
- return false;
- }
-
- return true;
-}
-
-bool ReadAheadThread::RunThread() {
- if (!InitializeFds()) {
- return false;
- }
-
- InitializeIter();
- InitializeBuffer();
-
- while (!IterDone()) {
- if (!ReadAheadIOStart()) {
- return false;
- }
-
- bool status = snapuserd_->WaitForMergeToComplete();
-
- if (status && !snapuserd_->CommitMerge(snapuserd_->GetTotalRaBlocksMerged())) {
- return false;
- }
-
- if (!status) break;
- }
-
- CloseFds();
- SNAP_LOG(INFO) << " ReadAhead thread terminating....";
- return true;
-}
-
-// Initialization
-bool ReadAheadThread::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- return true;
-}
-
-void ReadAheadThread::InitializeIter() {
- std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
- read_ahead_iter_ = read_ahead_ops.rbegin();
-}
-
-bool ReadAheadThread::IterDone() {
- std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
- return read_ahead_iter_ == read_ahead_ops.rend();
-}
-
-void ReadAheadThread::IterNext() {
- read_ahead_iter_++;
-}
-
-const CowOperation* ReadAheadThread::GetIterOp() {
- return *read_ahead_iter_;
-}
-
-void ReadAheadThread::InitializeBuffer() {
- void* mapped_addr = snapuserd_->GetMappedAddr();
- // Map the scratch space region into memory
- metadata_buffer_ =
- static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferMetadataOffset());
- read_ahead_buffer_ = static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
deleted file mode 100644
index 833969094..000000000
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <arpa/inet.h>
-#include <cutils/sockets.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-
-#include "snapuserd.h"
-#include "snapuserd_server.h"
-
-namespace android {
-namespace snapshot {
-
-DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
- if (input == "init") return DaemonOperations::INIT;
- if (input == "start") return DaemonOperations::START;
- if (input == "stop") return DaemonOperations::STOP;
- if (input == "query") return DaemonOperations::QUERY;
- if (input == "delete") return DaemonOperations::DELETE;
- if (input == "detach") return DaemonOperations::DETACH;
-
- return DaemonOperations::INVALID;
-}
-
-SnapuserdServer::~SnapuserdServer() {
- // Close any client sockets that were added via AcceptClient().
- for (size_t i = 1; i < watched_fds_.size(); i++) {
- close(watched_fds_[i].fd);
- }
-}
-
-std::string SnapuserdServer::GetDaemonStatus() {
- std::string msg = "";
-
- if (IsTerminating())
- msg = "passive";
- else
- msg = "active";
-
- return msg;
-}
-
-void SnapuserdServer::Parsemsg(std::string const& msg, const char delim,
- std::vector<std::string>& out) {
- std::stringstream ss(msg);
- std::string s;
-
- while (std::getline(ss, s, delim)) {
- out.push_back(s);
- }
-}
-
-void SnapuserdServer::ShutdownThreads() {
- StopThreads();
- JoinAllThreads();
-}
-
-DmUserHandler::DmUserHandler(std::shared_ptr<Snapuserd> snapuserd)
- : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
-
-bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
- ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));
- if (ret < 0) {
- PLOG(ERROR) << "Snapuserd:server: send() failed";
- return false;
- }
-
- if (ret < msg.size()) {
- LOG(ERROR) << "Partial send; expected " << msg.size() << " bytes, sent " << ret;
- return false;
- }
- return true;
-}
-
-bool SnapuserdServer::Recv(android::base::borrowed_fd fd, std::string* data) {
- char msg[MAX_PACKET_SIZE];
- ssize_t rv = TEMP_FAILURE_RETRY(recv(fd.get(), msg, sizeof(msg), 0));
- if (rv < 0) {
- PLOG(ERROR) << "recv failed";
- return false;
- }
- *data = std::string(msg, rv);
- return true;
-}
-
-bool SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {
- const char delim = ',';
-
- std::vector<std::string> out;
- Parsemsg(str, delim, out);
- DaemonOperations op = Resolveop(out[0]);
-
- switch (op) {
- case DaemonOperations::INIT: {
- // Message format:
- // init,<misc_name>,<cow_device_path>,<backing_device>
- //
- // Reads the metadata and send the number of sectors
- if (out.size() != 4) {
- LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- auto handler = AddHandler(out[1], out[2], out[3]);
- if (!handler) {
- return Sendmsg(fd, "fail");
- }
-
- auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
- return Sendmsg(fd, retval);
- }
- case DaemonOperations::START: {
- // Message format:
- // start,<misc_name>
- //
- // Start the new thread which binds to dm-user misc device
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Tried to re-attach control device: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!StartHandler(*iter)) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOperations::STOP: {
- // Message format: stop
- //
- // Stop all the threads gracefully and then shutdown the
- // main thread
- SetTerminating();
- ShutdownThreads();
- return true;
- }
- case DaemonOperations::QUERY: {
- // Message format: query
- //
- // As part of transition, Second stage daemon will be
- // created before terminating the first stage daemon. Hence,
- // for a brief period client may have to distiguish between
- // first stage daemon and second stage daemon.
- //
- // Second stage daemon is marked as active and hence will
- // be ready to receive control message.
- return Sendmsg(fd, GetDaemonStatus());
- }
- case DaemonOperations::DELETE: {
- // Message format:
- // delete,<misc_name>
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- if (!RemoveAndJoinHandler(out[1])) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOperations::DETACH: {
- terminating_ = true;
- return true;
- }
- default: {
- LOG(ERROR) << "Received unknown message type from client";
- Sendmsg(fd, "fail");
- return false;
- }
- }
-}
-
-void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
- LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
-
- if (!handler->snapuserd()->Start()) {
- LOG(ERROR) << " Failed to launch all worker threads";
- }
-
- handler->snapuserd()->CloseFds();
- handler->snapuserd()->CheckMergeCompletionStatus();
- handler->snapuserd()->UnmapBufferRegion();
-
- auto misc_name = handler->misc_name();
- LOG(INFO) << "Handler thread about to exit: " << misc_name;
-
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, handler->misc_name());
- if (iter == dm_users_.end()) {
- // RemoveAndJoinHandler() already removed us from the list, and is
- // now waiting on a join(), so just return. Additionally, release
- // all the resources held by snapuserd object which are shared
- // by worker threads. This should be done when the last reference
- // of "handler" is released; but we will explicitly release here
- // to make sure snapuserd object is freed as it is the biggest
- // consumer of memory in the daemon.
- handler->FreeResources();
- LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
- return;
- }
-
- LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
-
- if (handler->snapuserd()->IsAttached()) {
- handler->thread().detach();
- }
-
- // Important: free resources within the lock. This ensures that if
- // WaitForDelete() is called, the handler is either in the list, or
- // it's not and its resources are guaranteed to be freed.
- handler->FreeResources();
- }
-}
-
-bool SnapuserdServer::Start(const std::string& socketname) {
- sockfd_.reset(android_get_control_socket(socketname.c_str()));
- if (sockfd_ >= 0) {
- if (listen(sockfd_.get(), 4) < 0) {
- PLOG(ERROR) << "listen socket failed: " << socketname;
- return false;
- }
- } else {
- sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM));
- if (sockfd_ < 0) {
- PLOG(ERROR) << "Failed to create server socket " << socketname;
- return false;
- }
- }
-
- AddWatchedFd(sockfd_);
-
- LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
- return true;
-}
-
-bool SnapuserdServer::Run() {
- while (!IsTerminating()) {
- int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
- if (rv < 0) {
- PLOG(ERROR) << "poll failed";
- return false;
- }
- if (!rv) {
- continue;
- }
-
- if (watched_fds_[0].revents) {
- AcceptClient();
- }
-
- auto iter = watched_fds_.begin() + 1;
- while (iter != watched_fds_.end()) {
- if (iter->revents && !HandleClient(iter->fd, iter->revents)) {
- close(iter->fd);
- iter = watched_fds_.erase(iter);
- } else {
- iter++;
- }
- }
- }
-
- JoinAllThreads();
- return true;
-}
-
-void SnapuserdServer::JoinAllThreads() {
- // Acquire the thread list within the lock.
- std::vector<std::shared_ptr<DmUserHandler>> dm_users;
- {
- std::lock_guard<std::mutex> guard(lock_);
- dm_users = std::move(dm_users_);
- }
-
- for (auto& client : dm_users) {
- auto& th = client->thread();
-
- if (th.joinable()) th.join();
- }
-}
-
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
- struct pollfd p = {};
- p.fd = fd.get();
- p.events = POLLIN;
- watched_fds_.emplace_back(std::move(p));
-}
-
-void SnapuserdServer::AcceptClient() {
- int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "accept4 failed";
- return;
- }
-
- AddWatchedFd(fd);
-}
-
-bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
- if (revents & POLLHUP) {
- LOG(DEBUG) << "Snapuserd client disconnected";
- return false;
- }
-
- std::string str;
- if (!Recv(fd, &str)) {
- return false;
- }
- if (!Receivemsg(fd, str)) {
- LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
- return false;
- }
- return true;
-}
-
-void SnapuserdServer::Interrupt() {
- // Force close the socket so poll() fails.
- sockfd_ = {};
- SetTerminating();
-}
-
-std::shared_ptr<DmUserHandler> SnapuserdServer::AddHandler(const std::string& misc_name,
- const std::string& cow_device_path,
- const std::string& backing_device) {
- auto snapuserd = std::make_shared<Snapuserd>(misc_name, cow_device_path, backing_device);
- if (!snapuserd->InitCowDevice()) {
- LOG(ERROR) << "Failed to initialize Snapuserd";
- return nullptr;
- }
-
- if (!snapuserd->InitializeWorkers()) {
- LOG(ERROR) << "Failed to initialize workers";
- return nullptr;
- }
-
- auto handler = std::make_shared<DmUserHandler>(snapuserd);
- {
- std::lock_guard<std::mutex> lock(lock_);
- if (FindHandler(&lock, misc_name) != dm_users_.end()) {
- LOG(ERROR) << "Handler already exists: " << misc_name;
- return nullptr;
- }
- dm_users_.push_back(handler);
- }
- return handler;
-}
-
-bool SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
- if (handler->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Handler already attached";
- return false;
- }
-
- handler->snapuserd()->AttachControlDevice();
-
- handler->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, handler));
- return true;
-}
-
-auto SnapuserdServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
- const std::string& misc_name) -> HandlerList::iterator {
- CHECK(proof_of_lock);
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if ((*iter)->misc_name() == misc_name) {
- return iter;
- }
- }
- return dm_users_.end();
-}
-
-bool SnapuserdServer::RemoveAndJoinHandler(const std::string& misc_name) {
- std::shared_ptr<DmUserHandler> handler;
- {
- std::lock_guard<std::mutex> lock(lock_);
-
- auto iter = FindHandler(&lock, misc_name);
- if (iter == dm_users_.end()) {
- // Client already deleted.
- return true;
- }
- handler = std::move(*iter);
- dm_users_.erase(iter);
- }
-
- auto& th = handler->thread();
- if (th.joinable()) {
- th.join();
- }
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
deleted file mode 100644
index 6699189ea..000000000
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <poll.h>
-
-#include <cstdio>
-#include <cstring>
-#include <functional>
-#include <future>
-#include <iostream>
-#include <mutex>
-#include <sstream>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-#include "snapuserd.h"
-
-namespace android {
-namespace snapshot {
-
-static constexpr uint32_t MAX_PACKET_SIZE = 512;
-
-enum class DaemonOperations {
- INIT,
- START,
- QUERY,
- STOP,
- DELETE,
- DETACH,
- INVALID,
-};
-
-class DmUserHandler {
- public:
- explicit DmUserHandler(std::shared_ptr<Snapuserd> snapuserd);
-
- void FreeResources() {
- // Each worker thread holds a reference to snapuserd.
- // Clear them so that all the resources
- // held by snapuserd is released
- if (snapuserd_) {
- snapuserd_->FreeResources();
- snapuserd_ = nullptr;
- }
- }
- const std::shared_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
- std::thread& thread() { return thread_; }
-
- const std::string& misc_name() const { return misc_name_; }
-
- private:
- std::thread thread_;
- std::shared_ptr<Snapuserd> snapuserd_;
- std::string misc_name_;
-};
-
-class Stoppable {
- std::promise<void> exitSignal_;
- std::future<void> futureObj_;
-
- public:
- Stoppable() : futureObj_(exitSignal_.get_future()) {}
-
- virtual ~Stoppable() {}
-
- bool StopRequested() {
- // checks if value in future object is available
- if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) {
- return false;
- }
- return true;
- }
- // Request the thread to stop by setting value in promise object
- void StopThreads() { exitSignal_.set_value(); }
-};
-
-class SnapuserdServer : public Stoppable {
- private:
- android::base::unique_fd sockfd_;
- bool terminating_;
- std::vector<struct pollfd> watched_fds_;
-
- std::mutex lock_;
-
- using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
- HandlerList dm_users_;
-
- void AddWatchedFd(android::base::borrowed_fd fd);
- void AcceptClient();
- bool HandleClient(android::base::borrowed_fd fd, int revents);
- bool Recv(android::base::borrowed_fd fd, std::string* data);
- bool Sendmsg(android::base::borrowed_fd fd, const std::string& msg);
- bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
-
- void ShutdownThreads();
- bool RemoveAndJoinHandler(const std::string& control_device);
- DaemonOperations Resolveop(std::string& input);
- std::string GetDaemonStatus();
- void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
-
- bool IsTerminating() { return terminating_; }
-
- void RunThread(std::shared_ptr<DmUserHandler> handler);
- void JoinAllThreads();
-
- // Find a DmUserHandler within a lock.
- HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
- const std::string& misc_name);
-
- public:
- SnapuserdServer() { terminating_ = false; }
- ~SnapuserdServer();
-
- bool Start(const std::string& socketname);
- bool Run();
- void Interrupt();
-
- std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
- const std::string& cow_device_path,
- const std::string& backing_device);
- bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
-
- void SetTerminating() { terminating_ = true; }
-};
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
deleted file mode 100644
index 682f9da58..000000000
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ /dev/null
@@ -1,877 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd.h"
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#include <libsnapshot/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-void BufferSink::Initialize(size_t size) {
- buffer_size_ = size;
- buffer_offset_ = 0;
- buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void* BufferSink::GetPayloadBuffer(size_t size) {
- if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
- char* buffer = reinterpret_cast<char*>(GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
- return (char*)msg->payload.buf + buffer_offset_;
-}
-
-void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
- void* buf = GetPayloadBuffer(requested);
- if (!buf) {
- *actual = 0;
- return nullptr;
- }
- *actual = requested;
- return buf;
-}
-
-struct dm_user_header* BufferSink::GetHeaderPtr() {
- if (!(sizeof(struct dm_user_header) <= buffer_size_)) {
- return nullptr;
- }
- char* buf = reinterpret_cast<char*>(GetBufPtr());
- struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
- return header;
-}
-
-void* BufferSink::GetPayloadBufPtr() {
- char* buffer = reinterpret_cast<char*>(GetBufPtr());
- struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0]));
- return msg->payload.buf;
-}
-
-WorkerThread::WorkerThread(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- std::shared_ptr<Snapuserd> snapuserd) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = control_device;
- misc_name_ = misc_name;
- snapuserd_ = snapuserd;
- exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
-}
-
-bool WorkerThread::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::InitReader() {
- reader_ = std::make_unique<CowReader>();
- if (!reader_->InitForMerge(std::move(cow_fd_))) {
- return false;
- }
-
- return true;
-}
-
-// Construct kernel COW header in memory
-// This header will be in sector 0. The IO
-// request will always be 4k. After constructing
-// the header, zero out the remaining block.
-void WorkerThread::ConstructKernelCowHeader() {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-
- memset(buffer, 0, BLOCK_SZ);
-
- struct disk_header* dh = reinterpret_cast<struct disk_header*>(buffer);
-
- dh->magic = SNAP_MAGIC;
- dh->valid = SNAPSHOT_VALID;
- dh->version = SNAPSHOT_DISK_VERSION;
- dh->chunk_size = CHUNK_SIZE;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool WorkerThread::ProcessReplaceOp(const CowOperation* cow_op) {
- if (!reader_->ReadData(*cow_op, &bufsink_)) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::ReadFromBaseDevice(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
- return false;
- }
- SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
- << " Source: " << cow_op->source;
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
- cow_op->source * BLOCK_SZ)) {
- SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
- << "at block :" << cow_op->source;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::GetReadAheadPopulatedBuffer(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "GetReadAheadPopulatedBuffer: Failed to get payload buffer";
- return false;
- }
-
- if (!snapuserd_->GetReadAheadPopulatedBuffer(cow_op->new_block, buffer)) {
- return false;
- }
-
- return true;
-}
-
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool WorkerThread::ProcessCopyOp(const CowOperation* cow_op) {
- if (!GetReadAheadPopulatedBuffer(cow_op)) {
- SNAP_LOG(DEBUG) << " GetReadAheadPopulatedBuffer failed..."
- << " new_block: " << cow_op->new_block;
- if (!ReadFromBaseDevice(cow_op)) {
- return false;
- }
- }
-
- return true;
-}
-
-bool WorkerThread::ProcessZeroOp() {
- // Zero out the entire block
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
- return false;
- }
-
- memset(buffer, 0, BLOCK_SZ);
- return true;
-}
-
-bool WorkerThread::ProcessCowOp(const CowOperation* cow_op) {
- if (cow_op == nullptr) {
- SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
- return false;
- }
-
- switch (cow_op->type) {
- case kCowReplaceOp: {
- return ProcessReplaceOp(cow_op);
- }
-
- case kCowZeroOp: {
- return ProcessZeroOp();
- }
-
- case kCowCopyOp: {
- return ProcessCopyOp(cow_op);
- }
-
- default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- }
- }
- return false;
-}
-
-int WorkerThread::ReadUnalignedSector(
- sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
- size_t skip_sector_size = 0;
-
- SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
- << " Aligned sector: " << it->first;
-
- if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
- << " Aligned sector: " << it->first;
- return -1;
- }
-
- int num_sectors_skip = sector - it->first;
-
- if (num_sectors_skip > 0) {
- skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
- char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
- if (skip_sector_size == BLOCK_SZ) {
- SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
- << " Base-sector: " << it->first;
- return -1;
- }
-
- memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
- (BLOCK_SZ - skip_sector_size));
- }
-
- bufsink_.ResetBufferOffset();
- return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-/*
- * Read the data for a given COW Operation.
- *
- * Kernel can issue IO at a sector granularity.
- * Hence, an IO may end up with reading partial
- * data from a COW operation or we may also
- * end up with interspersed request between
- * two COW operations.
- *
- */
-int WorkerThread::ReadData(sector_t sector, size_t size) {
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator it;
- /*
- * chunk_map stores COW operation at 4k granularity.
- * If the requested IO with the sector falls on the 4k
- * boundary, then we can read the COW op directly without
- * any issue.
- *
- * However, if the requested sector is not 4K aligned,
- * then we will have the find the nearest COW operation
- * and chop the 4K block to fetch the requested sector.
- */
- it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
- Snapuserd::compare);
-
- if (!(it != chunk_vec.end())) {
- SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
- return -1;
- }
-
- // We didn't find the required sector; hence find the previous sector
- // as lower_bound will gives us the value greater than
- // the requested sector
- if (it->first != sector) {
- if (it != chunk_vec.begin()) {
- --it;
- }
-
- /*
- * If the IO is spanned between two COW operations,
- * split the IO into two parts:
- *
- * 1: Read the first part from the single COW op
- * 2: Read the second part from the next COW op.
- *
- * Ex: Let's say we have a 1024 Bytes IO request.
- *
- * 0 COW OP-1 4096 COW OP-2 8192
- * |******************|*******************|
- * |*****|*****|
- * 3584 4608
- * <- 1024B - >
- *
- * We have two COW operations which are 4k blocks.
- * The IO is requested for 1024 Bytes which are spanned
- * between two COW operations. We will split this IO
- * into two parts:
- *
- * 1: IO of size 512B from offset 3584 bytes (COW OP-1)
- * 2: IO of size 512B from offset 4096 bytes (COW OP-2)
- */
- return ReadUnalignedSector(sector, size, it);
- }
-
- int num_ops = DIV_ROUND_UP(size, BLOCK_SZ);
- sector_t read_sector = sector;
- while (num_ops) {
- // We have to make sure that the reads are
- // sequential; there shouldn't be a data
- // request merged with a metadata IO.
- if (it->first != read_sector) {
- SNAP_LOG(ERROR) << "Invalid IO request: read_sector: " << read_sector
- << " cow-op sector: " << it->first;
- return -1;
- } else if (!ProcessCowOp(it->second)) {
- return -1;
- }
- num_ops -= 1;
- read_sector += (BLOCK_SZ >> SECTOR_SHIFT);
-
- it++;
-
- if (it == chunk_vec.end() && num_ops) {
- SNAP_LOG(ERROR) << "Invalid IO request at sector " << sector
- << " COW ops completed; pending read-request: " << num_ops;
- return -1;
- }
- // Update the buffer offset
- bufsink_.UpdateBufferOffset(BLOCK_SZ);
- }
-
- // Reset the buffer offset
- bufsink_.ResetBufferOffset();
- return size;
-}
-
-/*
- * dm-snap does prefetch reads while reading disk-exceptions.
- * By default, prefetch value is set to 12; this means that
- * dm-snap will issue 12 areas wherein each area is a 4k page
- * of disk-exceptions.
- *
- * If during prefetch, if the chunk-id seen is beyond the
- * actual number of metadata page, fill the buffer with zero.
- * When dm-snap starts parsing the buffer, it will stop
- * reading metadata page once the buffer content is zero.
- */
-bool WorkerThread::ZerofillDiskExceptions(size_t read_size) {
- size_t size = exceptions_per_area_ * sizeof(struct disk_exception);
-
- if (read_size > size) {
- return false;
- }
-
- void* buffer = bufsink_.GetPayloadBuffer(size);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ZerofillDiskExceptions: Failed to get payload buffer";
- return false;
- }
-
- memset(buffer, 0, size);
- return true;
-}
-
-/*
- * A disk exception is a simple mapping of old_chunk to new_chunk.
- * When dm-snapshot device is created, kernel requests these mapping.
- *
- * Each disk exception is of size 16 bytes. Thus a single 4k page can
- * have:
- *
- * exceptions_per_area_ = 4096/16 = 256. This entire 4k page
- * is considered a metadata page and it is represented by chunk ID.
- *
- * Convert the chunk ID to index into the vector which gives us
- * the metadata page.
- */
-bool WorkerThread::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
- uint32_t stride = exceptions_per_area_ + 1;
- size_t size;
- const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
-
- // ChunkID to vector index
- lldiv_t divresult = lldiv(chunk, stride);
-
- if (divresult.quot < vec.size()) {
- size = exceptions_per_area_ * sizeof(struct disk_exception);
-
- if (read_size != size) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions: read_size: " << read_size
- << " does not match with size: " << size;
- return false;
- }
-
- void* buffer = bufsink_.GetPayloadBuffer(size);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions: Failed to get payload buffer of size: " << size;
- return false;
- }
-
- memcpy(buffer, vec[divresult.quot].get(), size);
- } else {
- return ZerofillDiskExceptions(read_size);
- }
-
- return true;
-}
-
-loff_t WorkerThread::GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
- int* unmerged_exceptions) {
- loff_t offset = 0;
- *unmerged_exceptions = 0;
-
- while (*unmerged_exceptions <= exceptions_per_area_) {
- struct disk_exception* merged_de =
- reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
- struct disk_exception* cow_de =
- reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
-
- // Unmerged op by the kernel
- if (merged_de->old_chunk != 0 || merged_de->new_chunk != 0) {
- if (!(merged_de->old_chunk == cow_de->old_chunk)) {
- SNAP_LOG(ERROR) << "GetMergeStartOffset: merged_de->old_chunk: "
- << merged_de->old_chunk
- << "cow_de->old_chunk: " << cow_de->old_chunk;
- return -1;
- }
-
- if (!(merged_de->new_chunk == cow_de->new_chunk)) {
- SNAP_LOG(ERROR) << "GetMergeStartOffset: merged_de->new_chunk: "
- << merged_de->new_chunk
- << "cow_de->new_chunk: " << cow_de->new_chunk;
- return -1;
- }
-
- offset += sizeof(struct disk_exception);
- *unmerged_exceptions += 1;
- continue;
- }
-
- break;
- }
-
- SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
- return offset;
-}
-
-int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
- int unmerged_exceptions, bool* copy_op, bool* commit) {
- int merged_ops_cur_iter = 0;
- std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
- *copy_op = false;
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-
- // Find the operations which are merged in this cycle.
- while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
- struct disk_exception* merged_de =
- reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
- struct disk_exception* cow_de =
- reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
-
- if (!(merged_de->new_chunk == 0)) {
- SNAP_LOG(ERROR) << "GetNumberOfMergedOps: Invalid new-chunk: " << merged_de->new_chunk;
- return -1;
- }
-
- if (!(merged_de->old_chunk == 0)) {
- SNAP_LOG(ERROR) << "GetNumberOfMergedOps: Invalid old-chunk: " << merged_de->old_chunk;
- return -1;
- }
-
- if (cow_de->new_chunk != 0) {
- merged_ops_cur_iter += 1;
- offset += sizeof(struct disk_exception);
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(ChunkToSector(cow_de->new_chunk), nullptr),
- Snapuserd::compare);
-
- if (!(it != chunk_vec.end())) {
- SNAP_LOG(ERROR) << "Sector not found: " << ChunkToSector(cow_de->new_chunk);
- return -1;
- }
-
- if (!(it->first == ChunkToSector(cow_de->new_chunk))) {
- SNAP_LOG(ERROR) << "Invalid sector: " << ChunkToSector(cow_de->new_chunk);
- return -1;
- }
- const CowOperation* cow_op = it->second;
-
- if (snapuserd_->IsReadAheadFeaturePresent() && cow_op->type == kCowCopyOp) {
- *copy_op = true;
- // Every single copy operation has to come from read-ahead
- // cache.
- if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) {
- SNAP_LOG(ERROR)
- << " Block: " << cow_op->new_block << " not found in read-ahead cache"
- << " Source: " << cow_op->source;
- return -1;
- }
- // If this is a final block merged in the read-ahead buffer
- // region, notify the read-ahead thread to make forward
- // progress
- if (cow_op->new_block == snapuserd_->GetFinalBlockMerged()) {
- *commit = true;
- }
- }
-
- // zero out to indicate that operation is merged.
- cow_de->old_chunk = 0;
- cow_de->new_chunk = 0;
- } else if (cow_de->old_chunk == 0) {
- // Already merged op in previous iteration or
- // This could also represent a partially filled area.
- //
- // If the op was merged in previous cycle, we don't have
- // to count them.
- break;
- } else {
- SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata: "
- << " merged_de-old-chunk: " << merged_de->old_chunk
- << " merged_de-new-chunk: " << merged_de->new_chunk
- << " cow_de-old-chunk: " << cow_de->old_chunk
- << " cow_de-new-chunk: " << cow_de->new_chunk
- << " unmerged_exceptions: " << unmerged_exceptions
- << " merged_ops_cur_iter: " << merged_ops_cur_iter
- << " offset: " << offset;
- return -1;
- }
- }
- return merged_ops_cur_iter;
-}
-
-bool WorkerThread::ProcessMergeComplete(chunk_t chunk, void* buffer) {
- uint32_t stride = exceptions_per_area_ + 1;
- const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
- bool copy_op = false;
- bool commit = false;
-
- // ChunkID to vector index
- lldiv_t divresult = lldiv(chunk, stride);
-
- if (!(divresult.quot < vec.size())) {
- SNAP_LOG(ERROR) << "ProcessMergeComplete: Invalid chunk: " << chunk
- << " Metadata-Index: " << divresult.quot << " Area-size: " << vec.size();
- return false;
- }
-
- SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
- << " Metadata-Index: " << divresult.quot;
-
- int unmerged_exceptions = 0;
- loff_t offset = GetMergeStartOffset(buffer, vec[divresult.quot].get(), &unmerged_exceptions);
-
- if (offset < 0) {
- SNAP_LOG(ERROR) << "GetMergeStartOffset failed: unmerged_exceptions: "
- << unmerged_exceptions;
- return false;
- }
-
- int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
- unmerged_exceptions, &copy_op, &commit);
-
- // There should be at least one operation merged in this cycle
- if (!(merged_ops_cur_iter > 0)) {
- SNAP_LOG(ERROR) << "Merge operation failed: " << merged_ops_cur_iter;
- return false;
- }
-
- if (copy_op) {
- if (commit) {
- // Push the flushing logic to read-ahead thread so that merge thread
- // can make forward progress. Sync will happen in the background
- snapuserd_->StartReadAhead();
- }
- } else {
- // Non-copy ops and all ops in older COW format
- if (!snapuserd_->CommitMerge(merged_ops_cur_iter)) {
- SNAP_LOG(ERROR) << "CommitMerge failed...";
- return false;
- }
- }
-
- SNAP_LOG(DEBUG) << "Merge success: " << merged_ops_cur_iter << "chunk: " << chunk;
- return true;
-}
-
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool WorkerThread::ReadDmUserHeader() {
- if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
- if (errno != ENOTBLK) {
- SNAP_PLOG(ERROR) << "Control-read failed";
- }
-
- return false;
- }
-
- return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool WorkerThread::WriteDmUserPayload(size_t size, bool header_response) {
- size_t payload_size = size;
- void* buf = bufsink_.GetPayloadBufPtr();
- if (header_response) {
- payload_size += sizeof(struct dm_user_header);
- buf = bufsink_.GetBufPtr();
- }
-
- if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
- SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::ReadDmUserPayload(void* buffer, size_t size) {
- if (!android::base::ReadFully(ctrl_fd_, buffer, size)) {
- SNAP_PLOG(ERROR) << "ReadDmUserPayload failed size: " << size;
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::DmuserWriteRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- // device mapper has the capability to allow
- // targets to flush the cache when writes are completed. This
- // is controlled by each target by a flag "flush_supported".
- // This flag is set by dm-user. When flush is supported,
- // a number of zero-length bio's will be submitted to
- // the target for the purpose of flushing cache. It is the
- // responsibility of the target driver - which is dm-user in this
- // case, to remap these bio's to the underlying device. Since,
- // there is no underlying device for dm-user, this zero length
- // bio's gets routed to daemon.
- //
- // Flush operations are generated post merge by dm-snap by having
- // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
- // to flush per se; hence, just respond back with a success message.
- if (header->sector == 0) {
- if (!(header->len == 0)) {
- SNAP_LOG(ERROR) << "Invalid header length received from sector 0: " << header->len;
- header->type = DM_USER_RESP_ERROR;
- } else {
- header->type = DM_USER_RESP_SUCCESS;
- }
-
- if (!WriteDmUserPayload(0, true)) {
- return false;
- }
- return true;
- }
-
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- size_t remaining_size = header->len;
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-
- chunk_t chunk = SectorToChunk(header->sector);
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(header->sector, nullptr), Snapuserd::compare);
-
- bool not_found = (it == chunk_vec.end() || it->first != header->sector);
-
- if (not_found) {
- void* buffer = bufsink_.GetPayloadBuffer(read_size);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "DmuserWriteRequest: Failed to get payload buffer of size: "
- << read_size;
- header->type = DM_USER_RESP_ERROR;
- } else {
- header->type = DM_USER_RESP_SUCCESS;
-
- if (!ReadDmUserPayload(buffer, read_size)) {
- SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- }
-
- if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
- SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- }
- }
- } else {
- SNAP_LOG(ERROR) << "DmuserWriteRequest: Invalid sector received: header->sector";
- header->type = DM_USER_RESP_ERROR;
- }
-
- if (!WriteDmUserPayload(0, true)) {
- return false;
- }
-
- return true;
-}
-
-bool WorkerThread::DmuserReadRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- size_t remaining_size = header->len;
- loff_t offset = 0;
- sector_t sector = header->sector;
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- bool header_response = true;
- do {
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-
- int ret = read_size;
- header->type = DM_USER_RESP_SUCCESS;
- chunk_t chunk = SectorToChunk(header->sector);
-
- // Request to sector 0 is always for kernel
- // representation of COW header. This IO should be only
- // once during dm-snapshot device creation. We should
- // never see multiple IO requests. Additionally this IO
- // will always be a single 4k.
- if (header->sector == 0) {
- if (read_size == BLOCK_SZ) {
- ConstructKernelCowHeader();
- SNAP_LOG(DEBUG) << "Kernel header constructed";
- } else {
- SNAP_LOG(ERROR) << "Invalid read_size: " << read_size << " for sector 0";
- header->type = DM_USER_RESP_ERROR;
- }
- } else {
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(header->sector, nullptr), Snapuserd::compare);
- bool not_found = (it == chunk_vec.end() || it->first != header->sector);
- if (!offset && (read_size == BLOCK_SZ) && not_found) {
- if (!ReadDiskExceptions(chunk, read_size)) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
- } else {
- chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
-
- ret = ReadData(sector + num_sectors_read, read_size);
- if (ret < 0) {
- SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
- << " Sector: " << (sector + num_sectors_read)
- << " size: " << read_size << " header-len: " << header->len;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
- }
- }
-
- // Just return the header if it is an error
- if (header->type == DM_USER_RESP_ERROR) {
- SNAP_LOG(ERROR) << "IO read request failed...";
- ret = 0;
- }
-
- if (!header_response) {
- CHECK(header->type == DM_USER_RESP_SUCCESS)
- << " failed for sector: " << sector << " header->len: " << header->len
- << " remaining_size: " << remaining_size;
- }
-
- // Daemon will not be terminated if there is any error. We will
- // just send the error back to dm-user.
- if (!WriteDmUserPayload(ret, header_response)) {
- return false;
- }
-
- if (header->type == DM_USER_RESP_ERROR) {
- break;
- }
-
- remaining_size -= ret;
- offset += ret;
- header_response = false;
- } while (remaining_size > 0);
-
- return true;
-}
-
-void WorkerThread::InitializeBufsink() {
- // Allocate the buffer which is used to communicate between
- // daemon and dm-user. The buffer comprises of header and a fixed payload.
- // If the dm-user requests a big IO, the IO will be broken into chunks
- // of PAYLOAD_SIZE.
- size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
- bufsink_.Initialize(buf_size);
-}
-
-bool WorkerThread::RunThread() {
- InitializeBufsink();
-
- if (!InitializeFds()) {
- return false;
- }
-
- if (!InitReader()) {
- return false;
- }
-
- // Start serving IO
- while (true) {
- if (!ProcessIORequest()) {
- break;
- }
- }
-
- CloseFds();
- reader_->CloseCowFd();
-
- return true;
-}
-
-bool WorkerThread::ProcessIORequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- if (!ReadDmUserHeader()) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
- SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
- SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
- SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
- SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
-
- switch (header->type) {
- case DM_USER_REQ_MAP_READ: {
- if (!DmuserReadRequest()) {
- return false;
- }
- break;
- }
-
- case DM_USER_REQ_MAP_WRITE: {
- if (!DmuserWriteRequest()) {
- return false;
- }
- break;
- }
- }
-
- return true;
-}
-
-} // namespace snapshot
-} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index e3e3af853..b07bf9182 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -23,7 +23,6 @@
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <openssl/sha.h>
-#include <payload_consumer/file_descriptor.h>
namespace android {
namespace snapshot {
@@ -128,79 +127,6 @@ bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size,
return true;
}
-bool WriteRandomData(ICowWriter* writer, std::string* hash) {
- unique_fd rand(open("/dev/urandom", O_RDONLY));
- if (rand < 0) {
- PLOG(ERROR) << "open /dev/urandom";
- return false;
- }
-
- SHA256_CTX ctx;
- if (hash) {
- SHA256_Init(&ctx);
- }
-
- if (!writer->options().max_blocks) {
- LOG(ERROR) << "CowWriter must specify maximum number of blocks";
- return false;
- }
- uint64_t num_blocks = writer->options().max_blocks.value();
-
- size_t block_size = writer->options().block_size;
- std::string block(block_size, '\0');
- for (uint64_t i = 0; i < num_blocks; i++) {
- if (!ReadFully(rand, block.data(), block.size())) {
- PLOG(ERROR) << "read /dev/urandom";
- return false;
- }
- if (!writer->AddRawBlocks(i, block.data(), block.size())) {
- LOG(ERROR) << "Failed to add raw block " << i;
- return false;
- }
- if (hash) {
- SHA256_Update(&ctx, block.data(), block.size());
- }
- }
-
- if (hash) {
- uint8_t out[32];
- SHA256_Final(out, &ctx);
- *hash = ToHexString(out, sizeof(out));
- }
- return true;
-}
-
-std::string HashSnapshot(ISnapshotWriter* writer) {
- auto reader = writer->OpenReader();
- if (!reader) {
- return {};
- }
-
- SHA256_CTX ctx;
- SHA256_Init(&ctx);
-
- uint64_t remaining = reader->BlockDevSize();
- char buffer[4096];
- while (remaining) {
- size_t to_read =
- static_cast<size_t>(std::min(remaining, static_cast<uint64_t>(sizeof(buffer))));
- ssize_t read = reader->Read(&buffer, to_read);
- if (read <= 0) {
- if (read < 0) {
- LOG(ERROR) << "Failed to read from snapshot writer";
- return {};
- }
- break;
- }
- SHA256_Update(&ctx, buffer, to_read);
- remaining -= static_cast<size_t>(read);
- }
-
- uint8_t out[32];
- SHA256_Final(out, &ctx);
- return ToHexString(out, sizeof(out));
-}
-
std::optional<std::string> GetHash(const std::string& path) {
std::string content;
if (!android::base::ReadFileToString(path, &content, true)) {
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
deleted file mode 100644
index 69d72e19c..000000000
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// A subset of system/update_engine/update_metadata.proto. A separate file is
-// used here because:
-// - The original file is optimized for LITE_RUNTIME, but fuzzing needs
-// reflection.
-// - The definition here has less fields. libsnapshot only uses fields declared
-// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If
-// libsnapshot uses more fields in system/update_engine/update_metadata.proto
-// in the future, they must be added here too, otherwise it will fail to
-// compile.
-//
-// It is okay that this file is older than
-// system/update_engine/update_metadata.proto as long as the messages defined
-// here can also be parsed by protobuf defined there. However, it is not
-// okay to add fields here without adding them to
-// system/update_engine/update_metadata.proto. Doing so will cause a compiler
-// error when libsnapshot code starts to use these dangling fields.
-
-syntax = "proto2";
-
-package chromeos_update_engine;
-
-message Extent {
- optional uint64 start_block = 1;
- optional uint64 num_blocks = 2;
-}
-
-message PartitionInfo {
- optional uint64 size = 1;
-}
-
-message InstallOperation {
- enum Type {
- SOURCE_COPY = 4;
- // Not used by libsnapshot. Declared here so that the fuzzer has an
- // alternative value to use for |type|.
- ZERO = 6;
- }
- required Type type = 1;
- repeated Extent src_extents = 4;
- repeated Extent dst_extents = 6;
-}
-
-message PartitionUpdate {
- required string partition_name = 1;
- optional PartitionInfo new_partition_info = 7;
- repeated InstallOperation operations = 8;
- optional Extent hash_tree_extent = 11;
- optional Extent fec_extent = 15;
- optional uint64 estimate_cow_size = 19;
-}
-
-message DynamicPartitionGroup {
- required string name = 1;
- optional uint64 size = 2;
- repeated string partition_names = 3;
-}
-
-message DynamicPartitionMetadata {
- repeated DynamicPartitionGroup groups = 1;
- optional bool vabc_enabled = 3;
- optional string vabc_compression_param = 4;
- optional uint32 cow_version = 5;
-}
-
-message DeltaArchiveManifest {
- repeated PartitionUpdate partitions = 13;
- optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
- optional bool partial_update = 16;
-}
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 4a2af1c10..d32b61eaa 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -22,7 +22,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/roots.h>
@@ -92,7 +91,7 @@ AutoDeleteSnapshot::~AutoDeleteSnapshot() {
}
}
-Return InitializeKernelCow(const std::string& device) {
+Return InitializeCow(const std::string& device) {
// When the kernel creates a persistent dm-snapshot, it requires a CoW file
// to store the modifications. The kernel interface does not specify how
// the CoW is used, and there is no standard associated.
@@ -183,17 +182,5 @@ void AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uin
new_extent->set_num_blocks(num_blocks);
}
-bool IsCompressionEnabled() {
- return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
-}
-
-std::string GetOtherPartitionName(const std::string& name) {
- auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
- CHECK(suffix == "_a" || suffix == "_b");
-
- auto other_suffix = (suffix == "_a") ? "_b" : "_a";
- return name.substr(0, name.size() - suffix.size()) + other_suffix;
-}
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 671de9dbc..e69bdadc9 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -112,7 +112,7 @@ std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
// Initialize a device before using it as the COW device for a dm-snapshot device.
-Return InitializeKernelCow(const std::string& device);
+Return InitializeCow(const std::string& device);
// "Atomically" write string to file. This is done by a series of actions:
// 1. Write to path + ".tmp"
@@ -129,10 +129,5 @@ std::ostream& operator<<(std::ostream& os, const Now&);
void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
uint64_t start_block, uint64_t num_blocks);
-bool IsCompressionEnabled();
-
-// Swap the suffix of a partition name.
-std::string GetOtherPartitionName(const std::string& name);
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index 5b0716851..beb18ef83 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -1,16 +1,7 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libstorage_literals_headers",
host_supported: true,
recovery_available: true,
export_include_dirs: ["."],
- target: {
- windows: {
- enabled: true,
- },
- },
}
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
index c1970978e..bceababea 100644
--- a/fs_mgr/libvbmeta/Android.bp
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
libvbmeta_lib_deps = [
"libbase",
"libcrypto",
@@ -54,11 +50,4 @@ cc_test_host {
"avbtool",
"vbmake",
],
- data: [
- "data/*",
- ],
- // Not unit tests due to several binary and lib dependencies currently hard to replicate in continuous execution
- test_options: {
- unit_test: false,
- },
-}
+} \ No newline at end of file
diff --git a/fs_mgr/libvbmeta/data/testkey_rsa2048.pem b/fs_mgr/libvbmeta/data/testkey_rsa2048.pem
deleted file mode 100644
index 867dcff54..000000000
--- a/fs_mgr/libvbmeta/data/testkey_rsa2048.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
-4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
-gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
-DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
-uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
-YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
-SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
-jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
-z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
-mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
-o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
-zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
-5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
-BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
-vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
-i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
-iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
-mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
-b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
-oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
-lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
-nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
-PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
-vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
-GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
------END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libvbmeta/super_vbmeta_test.cpp b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
index 7329a6190..daed0d1b3 100644
--- a/fs_mgr/libvbmeta/super_vbmeta_test.cpp
+++ b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
@@ -54,7 +54,7 @@ void GeneratePartitionImage(int fd, const std::string& file_name,
cmd << "avbtool add_hashtree_footer"
<< " --image " << file_name << " --partition_name " << partition_name
<< " --partition_size " << FAKE_PARTITION_SIZE << " --algorithm SHA256_RSA2048"
- << " --key data/testkey_rsa2048.pem";
+ << " --key external/avb/test/data/testkey_rsa2048.pem";
int rc = system(cmd.str().c_str());
EXPECT_TRUE(WIFEXITED(rc));
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 335da9e61..f68ab87cc 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -12,15 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_test {
name: "CtsFsMgrTestCases",
test_suites: [
"cts",
"device-tests",
+ "vts10",
],
compile_multilib: "both",
multilib: {
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index de835b3ad..0ff89957a 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -21,9 +21,6 @@
<option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- <option name="throw-on-error" value="false" />
- </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsFsMgrTestCases" />
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 9542bc167..82c42629d 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -15,17 +15,13 @@ USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
adb remount tests
--c --color Dress output with highlighting colors
--h --help This help
--D --no-wait-screen Do not wait for display screen to settle
--t --print-time Report the test duration
--s --serial Specify device (must if multiple are present)"
-if [ -n "`which timeout`" ]; then
- USAGE="${USAGE}
--a --wait-adb <duration> adb wait timeout
--f --wait-fastboot <duration> fastboot wait timeout"
-fi
-USAGE="${USAGE}
+--color Dress output with highlighting colors
+--help This help
+--no-wait-screen Do not wait for display screen to settle
+--print-time Report the test duration
+--serial Specify device (must if multiple are present)
+--wait-adb <duration> adb wait timeout
+--wait-fastboot <duration> fastboot wait timeout
Conditions:
- Must be a userdebug build.
@@ -50,10 +46,10 @@ TAB="`echo | tr '\n' '\t'`"
ESCAPE="`echo | tr '\n' '\033'`"
# A _real_ embedded carriage return character
CR="`echo | tr '\n' '\r'`"
-GREEN="${ESCAPE}[32m"
-RED="${ESCAPE}[31m"
-YELLOW="${ESCAPE}[33m"
-BLUE="${ESCAPE}[34m"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+ORANGE="${ESCAPE}[38;5;255:165:0m"
+BLUE="${ESCAPE}[35m"
NORMAL="${ESCAPE}[0m"
TMPDIR=${TMPDIR:-/tmp}
print_time=false
@@ -76,7 +72,7 @@ inFastboot() {
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
- wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null
+ wc -l | grep '^1$' >/dev/null
fi
}
@@ -89,7 +85,7 @@ inAdb() {
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
- wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null
+ wc -l | grep '^1$' >/dev/null
fi
}
@@ -104,7 +100,7 @@ inRecovery() {
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
return ${?}
fi
- if echo "${list}" | wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null; then
+ if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
echo "${list}" |
grep "[${SPACE}${TAB}]recovery\$" >/dev/null
return ${?}
@@ -147,7 +143,7 @@ adb_logcat() {
adb logcat "${@}" </dev/null |
tr -d '\r' |
grep -v 'logd : logdr: UID=' |
- sed -e '${ /------- beginning of kernel/d }' -e 's/^[0-1][0-9]-[0-3][0-9] //'
+ sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
}
[ "USAGE: avc_check >/dev/stderr
@@ -164,7 +160,7 @@ avc_check() {
if [ -z "${L}" ]; then
return
fi
- echo "${YELLOW}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
echo "${L}" | sed "s/^/${INDENT}/" >&2
}
@@ -213,13 +209,6 @@ adb_ls() {
return ${ret}
}
-[ "USAGE: adb_test <expression>
-
-Returns: exit status of the test expression" ]
-adb_test() {
- adb_sh test "${@}" </dev/null
-}
-
[ "USAGE: adb_reboot
Returns: true if the reboot command succeeded" ]
@@ -295,7 +284,7 @@ adb_wait() {
local start=`date +%s`
local duration=
local ret
- if [ -n "${1}" -a -n "`which timeout`" ]; then
+ if [ -n "${1}" ]; then
USB_DEVICE=`usb_devnum --next`
duration=`format_duration ${1}`
echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
@@ -310,7 +299,7 @@ adb_wait() {
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
fi
fi
local end=`date +%s`
@@ -370,22 +359,18 @@ usb_status() {
echo "(In adb mode `adb_user`)"
else
echo "(USB stack borken for ${USB_ADDRESS})"
- if [ -n "`which usb_devnum`" ]; then
- USB_DEVICE=`usb_devnum`
- if [ -n "`which lsusb`" ]; then
- if [ -n "${USB_DEVICE}" ]; then
- echo "# lsusb -v -s ${USB_DEVICE#dev}"
- local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
- if [ -n "${D}" ]; then
- echo "${D}"
- else
- lsusb -v
- fi
- else
- echo "# lsusb -v (expected device missing)"
- lsusb -v
- fi
+ USB_DEVICE=`usb_devnum`
+ if [ -n "${USB_DEVICE}" ]; then
+ echo "# lsusb -v -s ${USB_DEVICE#dev}"
+ local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
+ if [ -n "${D}" ]; then
+ echo "${D}"
+ else
+ lsusb -v
fi
+ else
+ echo "# lsusb -v (expected device missing)"
+ lsusb -v
fi >&2
fi
}
@@ -397,7 +382,7 @@ fastboot_wait() {
local ret
# fastboot has no wait-for-device, but it does an automatic
# wait and requires (even a nonsensical) command to do so.
- if [ -n "${1}" -a -n "`which timeout`" ]; then
+ if [ -n "${1}" ]; then
USB_DEVICE=`usb_devnum --next`
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
@@ -413,7 +398,7 @@ fastboot_wait() {
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi >&2
fi
return ${ret}
@@ -424,7 +409,7 @@ fastboot_wait() {
Returns: waits until the device has returned for recovery or optional timeout" ]
recovery_wait() {
local ret
- if [ -n "${1}" -a -n "`which timeout`" ]; then
+ if [ -n "${1}" ]; then
USB_DEVICE=`usb_devnum --next`
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
@@ -438,7 +423,7 @@ recovery_wait() {
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi >&2
fi
return ${ret}
@@ -735,46 +720,22 @@ check_ne() {
fi
}
-[ "USAGE: join_with <delimiter> <strings>
-
-Joins strings with delimiter" ]
-join_with() {
- if [ "${#}" -lt 2 ]; then
- echo
- return
- fi
- local delimiter="${1}"
- local result="${2}"
- shift 2
- for element in "${@}"; do
- result+="${delimiter}${element}"
- done
- echo "${result}"
-}
-
[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
skip_administrative_mounts() {
- local exclude_filesystems=(
- "overlay" "tmpfs" "none" "sysfs" "proc" "selinuxfs" "debugfs" "bpf"
- "binfmt_misc" "cg2_bpf" "pstore" "tracefs" "adb" "mtp" "ptp" "devpts"
- "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs"
- )
- local exclude_devices=(
- "\/sys\/kernel\/debug" "\/data\/media" "\/dev\/block\/loop[0-9]*"
- "${exclude_filesystems[@]}"
- )
- local exclude_mount_points=(
- "\/cache" "\/mnt\/scratch" "\/mnt\/vendor\/persist" "\/persist"
- "\/metadata"
- )
if [ "data" = "${1}" ]; then
- exclude_mount_points+=("\/data")
- fi
- awk '$1 !~ /^('"$(join_with "|" "${exclude_devices[@]}")"')$/ &&
- $2 !~ /^('"$(join_with "|" "${exclude_mount_points[@]}")"')$/ &&
- $3 !~ /^('"$(join_with "|" "${exclude_filesystems[@]}")"')$/'
+ grep -v " /data "
+ else
+ cat -
+ fi |
+ grep -v \
+ -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
+ -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e " functionfs " \
+ -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
+ -e "^rootfs / rootfs rw," \
+ -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
}
[ "USAGE: skip_unrelated_mounts < /proc/mounts
@@ -785,35 +746,20 @@ Filters out all apex and vendor override administrative overlay mounts
uninteresting to the test" ]
skip_unrelated_mounts() {
grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
- grep -v "[%] /\(data_mirror\|apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+ grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
}
##
## MAINLINE
##
-HOSTOS=`uname`
-GETOPTS="--alternative --unquoted
- --longoptions help,serial:,colour,color,no-colour,no-color
- --longoptions wait-adb:,wait-fastboot:
- --longoptions wait-screen,wait-display
- --longoptions no-wait-screen,no-wait-display
- --longoptions gtest_print_time,print-time
- --"
-if [ "Darwin" = "${HOSTOS}" ]; then
- GETOPTS=
- USAGE="`echo \"${USAGE}\" |
- sed 's/--color/ /g
- 1s/--help/-h/
- s/--help/ /g
- s/--no-wait-screen/ /g
- s/--print-time/ /g
- 1s/--serial/-s/
- s/--serial/ /g
- s/--wait-adb/ /g
- s/--wait-fastboot/ /g'`"
-fi
-OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:t" ${*}` ||
+OPTIONS=`getopt --alternative --unquoted \
+ --longoptions help,serial:,colour,color,no-colour,no-color \
+ --longoptions wait-adb:,wait-fastboot: \
+ --longoptions wait-screen,wait-display \
+ --longoptions no-wait-screen,no-wait-display \
+ --longoptions gtest_print_time,print-time \
+ -- "?hs:" ${*}` ||
( echo "${USAGE}" >&2 ; false ) ||
die "getopt failure"
set -- ${OPTIONS}
@@ -829,26 +775,26 @@ while [ ${#} -gt 0 ]; do
export ANDROID_SERIAL=${2}
shift
;;
- -c | --color | --colour)
+ --color | --colour)
color=true
;;
- -C | --no-color | --no-colour)
+ --no-color | --no-colour)
color=false
;;
- -D | --no-wait-display | --no-wait-screen)
+ --no-wait-display | --no-wait-screen)
screen_wait=false
;;
- -d | --wait-display | --wait-screen)
+ --wait-display | --wait-screen)
screen_wait=true
;;
- -t | --print-time | --gtest_print_time)
+ --print-time | --gtest_print_time)
print_time=true
;;
- -a | --wait-adb)
+ --wait-adb)
ADB_WAIT=${2}
shift
;;
- -f | --wait-fastboot)
+ --wait-fastboot)
FASTBOOT_WAIT=${2}
shift
;;
@@ -869,14 +815,11 @@ done
if ! ${color}; then
GREEN=""
RED=""
- YELLOW=""
+ ORANGE=""
BLUE=""
NORMAL=""
fi
-# Set an ERR trap handler to report any unhandled error
-trap 'die "line ${LINENO}: unhandled error"' ERR
-
if ${print_time}; then
echo "${BLUE}[ INFO ]${NORMAL}" start `date` >&2
fi
@@ -884,14 +827,14 @@ fi
inFastboot && die "device in fastboot mode"
inRecovery && die "device in recovery mode"
if ! inAdb; then
- echo "${YELLOW}[ WARNING ]${NORMAL} device not in adb mode" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2
adb_wait ${ADB_WAIT}
fi
inAdb || die "specified device not in adb mode"
isDebuggable || die "device not a debug build"
enforcing=true
if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
- echo "${YELLOW}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
enforcing=false
fi
@@ -903,13 +846,9 @@ D=`get_property ro.serialno`
[ -n "${D}" ] || D=`get_property ro.boot.serialno`
[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
USB_SERIAL=
-if [ -n "${ANDROID_SERIAL}" -a "Darwin" != "${HOSTOS}" ]; then
- USB_SERIAL="`find /sys/devices -name serial | grep usb || true`"
- if [ -n "${USB_SERIAL}" ]; then
- USB_SERIAL=`echo "${USB_SERIAL}" |
- xargs grep -l ${ANDROID_SERIAL} || true`
- fi
-fi
+[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
+ grep usb |
+ xargs -r grep -l ${ANDROID_SERIAL}`
USB_ADDRESS=
if [ -n "${USB_SERIAL}" ]; then
USB_ADDRESS=${USB_SERIAL%/serial}
@@ -921,20 +860,15 @@ fi
BUILD_DESCRIPTION=`get_property ro.build.description`
[ -z "${BUILD_DESCRIPTION}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
-KERNEL_VERSION="`adb_su cat /proc/version </dev/null 2>/dev/null`"
-[ -z "${KERNEL_VERSION}" ] ||
- echo "${BLUE}[ INFO ]${NORMAL} ${KERNEL_VERSION}" >&2
ACTIVE_SLOT=`get_active_slot`
[ -z "${ACTIVE_SLOT}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
# Acquire list of system partitions
-# KISS (assume system partition mount point is "/<partition name>")
-PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
- grep -v "^[#${SPACE}${TAB}]" |
+PARTITIONS=`adb_su cat /vendor/etc/fstab* |
skip_administrative_mounts |
- awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
+ sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
sort -u |
tr '\n' ' '`
PARTITIONS="${PARTITIONS:-system vendor}"
@@ -969,12 +903,9 @@ adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
done
# If reboot too soon after fresh flash, could trip device update failure logic
-if ${screen_wait}; then
- echo "${YELLOW}[ WARNING ]${NORMAL} waiting for screen to come up. Consider --no-wait-screen option" >&2
-fi
if ! wait_for_screen && ${screen_wait}; then
screen_wait=false
- echo "${YELLOW}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
fi
# Can we test remount -R command?
@@ -991,7 +922,7 @@ if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
if inAdb; then
reboot=false
for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay; then
+ if adb_su ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
adb_su rm -rf /${d}/overlay </dev/null
reboot=true
fi
@@ -1023,7 +954,7 @@ else
adb_su remount -R system </dev/null
err=${?}
if [ "${err}" != 0 ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
T="-t ${T}"
else
# Rebooted, logcat will be meaningless, and last logcat will likely be clear
@@ -1045,23 +976,20 @@ fi
echo "${GREEN}[ RUN ]${NORMAL} Testing kernel support for overlayfs" >&2
adb_wait || die "wait for device failed"
-adb_root ||
- die "initial setup"
-
-adb_test -d /sys/module/overlay ||
+adb_sh ls -d /sys/module/overlay </dev/null >/dev/null 2>/dev/null ||
adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
(
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
false
) ||
overlayfs_supported=false
if ${overlayfs_supported}; then
- adb_test -f /sys/module/overlay/parameters/override_creds &&
+ adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null 2>/dev/null &&
echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
case `adb_sh uname -r </dev/null` in
4.[456789].* | 4.[1-9][0-9]* | [56789].*)
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
overlayfs_supported=false
;;
*)
@@ -1070,6 +998,9 @@ if ${overlayfs_supported}; then
esac
fi
+adb_root ||
+ die "initial setup"
+
echo "${GREEN}[ RUN ]${NORMAL} Checking current overlayfs status" >&2
# We can not universally use adb enable-verity to ensure device is
@@ -1079,15 +1010,15 @@ echo "${GREEN}[ RUN ]${NORMAL} Checking current overlayfs status" >&2
# having to go through enable-verity transition.
reboot=false
for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay; then
- echo "${YELLOW}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
+ if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
adb_sh rm -rf /${d}/overlay </dev/null ||
die "/${d}/overlay wipe"
reboot=true
fi
done
if ${reboot}; then
- echo "${YELLOW}[ WARNING ]${NORMAL} rebooting before test" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
adb_reboot &&
adb_wait ${ADB_WAIT} ||
die "lost device after reboot after wipe `usb_status`"
@@ -1099,7 +1030,7 @@ D=`adb_sh df -k </dev/null` &&
D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
echo "${H}" &&
echo "${D}" &&
- echo "${YELLOW}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
overlayfs_needed=true
D=`adb_sh cat /proc/mounts </dev/null |
@@ -1152,7 +1083,7 @@ rebooted=false
if [ X"${D}" != X"${H}" ]; then
echo "${H}"
if [ X"${D}" != X"${D##*setup failed}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
fi
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
@@ -1199,14 +1130,14 @@ if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
elif ${rebooted}; then
echo "${GREEN}[ OK ]${NORMAL} verity already disabled" >&2
else
- echo "${YELLOW}[ WARNING ]${NORMAL} verity already disabled" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} verity already disabled" >&2
fi
echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
# Feed log with selinux denials as baseline before overlays
adb_unroot
-adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
adb_root
D=`adb remount 2>&1`
@@ -1229,7 +1160,7 @@ if ${overlayfs_needed}; then
die -t ${T} "overlay takeover failed"
fi
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
if [ -z "${virtual_ab}" ]; then
scratch_partition=scratch
fi
@@ -1255,7 +1186,7 @@ if ${overlayfs_needed}; then
die "scratch size"
echo "${BLUE}[ INFO ]${NORMAL} scratch size ${scratch_size}KB" >&2
for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay/system/upper; then
+ if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>/dev/null; then
echo "${BLUE}[ INFO ]${NORMAL} /${d}/overlay is setup" >&2
fi
done
@@ -1336,11 +1267,11 @@ else
fi
check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- echo "${YELLOW}[ WARNING ]${NORMAL} system devt ${SYSTEM_DEVT} major 0" >&2
+ die "system devt ${SYSTEM_DEVT} is major 0"
[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor devt ${VENDOR_DEVT} major 0" >&2
+ die "vendor devt ${SYSTEM_DEVT} is major 0"
-# Download libc.so, append some garbage, push back, and check if the file
+# Download libc.so, append some gargage, push back, and check if the file
# is updated.
tempdir="`mktemp -d`"
cleanup() {
@@ -1348,8 +1279,8 @@ cleanup() {
}
adb pull /system/lib/bootstrap/libc.so ${tempdir} >/dev/null ||
die "pull libc.so from device"
-garbage="D105225BBFCB1EB8AB8EBDB7094646F0"
-echo "${garbage}" >> ${tempdir}/libc.so
+garbage="`hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random`"
+echo ${garbage} >> ${tempdir}/libc.so
adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so >/dev/null ||
die "push libc.so to device"
adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
@@ -1361,7 +1292,7 @@ echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2
fixup_from_recovery() {
inRecovery || return 1
- echo "${YELLOW}[ ERROR ]${NORMAL} Device in recovery" >&2
+ echo "${ORANGE}[ ERROR ]${NORMAL} Device in recovery" >&2
adb reboot </dev/null
adb_wait ${ADB_WAIT}
}
@@ -1381,7 +1312,7 @@ if ${overlayfs_needed}; then
adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
skip_administrative_mounts |
grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
fi
@@ -1393,7 +1324,7 @@ if ${enforcing}; then
echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
# Feed unprivileged log with selinux denials as a result of overlays
wait_for_screen
- adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
+ adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
fi
# If overlayfs has a nested security problem, this will fail.
B="`adb_ls /system/`" ||
@@ -1415,12 +1346,12 @@ check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" s
check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
-check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" --warning base vendor devt after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
# Feed log with selinux denials as a result of overlays
-adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
# Check if the updated libc.so is persistent after reboot.
adb_root &&
@@ -1442,20 +1373,20 @@ is_bootloader_fastboot=false
is_userspace_fastboot=false
if ! ${is_bootloader_fastboot}; then
- echo "${YELLOW}[ WARNING ]${NORMAL} does not support fastboot, skipping"
+ echo "${ORANGE}[ WARNING ]${NORMAL} does not support fastboot, skipping"
elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} build tree not setup, skipping"
+ echo "${ORANGE}[ WARNING ]${NORMAL} build tree not setup, skipping"
elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor image missing, skipping"
+ echo "${ORANGE}[ WARNING ]${NORMAL} vendor image missing, skipping"
elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} wrong vendor image, skipping"
+ echo "${ORANGE}[ WARNING ]${NORMAL} wrong vendor image, skipping"
elif [ -z "${ANDROID_HOST_OUT}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} please run lunch, skipping"
+ echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
elif ! (
adb_cat /vendor/build.prop |
cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
) >/dev/null 2>/dev/null; then
- echo "${YELLOW}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+ echo "${ORANGE}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
else
wait_for_screen
avc_check
@@ -1501,7 +1432,7 @@ else
fi
fastboot reboot ||
die "can not reboot out of fastboot"
- echo "${YELLOW}[ WARNING ]${NORMAL} adb after fastboot"
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb after fastboot"
adb_wait ${ADB_WAIT} ||
fixup_from_recovery ||
die "did not reboot after formatting ${scratch_partition} `usb_status`"
@@ -1518,8 +1449,8 @@ else
if ${is_userspace_fastboot}; then
die "overlay supposed to be minus /vendor takeover after flash vendor"
else
- echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
- echo "${YELLOW}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
fi
fi
B="`adb_cat /system/hello`"
@@ -1537,15 +1468,15 @@ else
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
vendor content after flash vendor
else
- echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
--warning vendor content after flash vendor
fi
check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
- check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
fi
@@ -1558,7 +1489,7 @@ err=${?}
L=
D="${H%?Now reboot your device for settings to take effect*}"
if [ X"${H}" != X"${D}" ]; then
- echo "${YELLOW}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
adb_reboot &&
adb_wait ${ADB_WAIT} &&
@@ -1616,7 +1547,7 @@ if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
err=${?}
if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
then
- echo "${YELLOW}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
adb_reboot &&
adb_wait ${ADB_WAIT} &&
adb_root ||
@@ -1649,9 +1580,9 @@ fixup_from_fastboot() {
if [ -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${YELLOW}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${ORANGE}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
else
- echo "${YELLOW}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
+ echo "${ORANGE}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
fi >&2
fastboot --set-active=${ACTIVE_SLOT}
fi
@@ -1691,10 +1622,8 @@ echo "${GREEN}[ OK ]${NORMAL} remount command works from setup" >&2
# This also saves a lot of 'noise' from the command doing a mkfs on backing
# storage and all the related tuning and adjustment.
for d in ${OVERLAYFS_BACKING}; do
- if adb_test -d /${d}/overlay; then
- adb_su rm -rf /${d}/overlay </dev/null ||
- die "/${d}/overlay wipe"
- fi
+ adb_su rm -rf /${d}/overlay </dev/null ||
+ die "/${d}/overlay wipe"
done
adb_reboot &&
adb_wait ${ADB_WAIT} ||
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 953574b66..46f1c590c 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -119,105 +119,10 @@ const std::vector<std::pair<std::string, std::string>> result_space = {
{"terminator", "truncated"},
};
-const std::string bootconfig =
- "androidboot.bootdevice = \" \"1d84000.ufshc\"\n"
- "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
- "androidboot.baseband = \"sdy\"\n"
- "androidboot.keymaster = \"1\"\n"
- "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
- "androidboot.slot_suffix = \"_a\"\n"
- "androidboot.hardware.platform = \"sdw813\"\n"
- "androidboot.hardware = \"foo\"\n"
- "androidboot.revision = \"EVT1.0\"\n"
- "androidboot.bootloader = \"burp-0.1-7521\"\n"
- "androidboot.hardware.sku = \"mary\"\n"
- "androidboot.hardware.radio.subtype = \"0\"\n"
- "androidboot.dtbo_idx = \"2\"\n"
- "androidboot.mode = \"normal\"\n"
- "androidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\n"
- "androidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\n"
- "androidboot.hardware.ufs = \"2GB,combushi\"\n"
- "androidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\n"
- "androidboot.ramdump = \"disabled\"\n"
- "androidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\n"
- "androidboot.vbmeta.avb_version = \"1.1\"\n"
- "androidboot.vbmeta.device_state = \"unlocked\"\n"
- "androidboot.vbmeta.hash_alg = \"sha256\"\n"
- "androidboot.vbmeta.size = \"5248\"\n"
- "androidboot.vbmeta.digest = \""
- "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\n"
- "androidboot.vbmeta.invalidate_on_error = \"yes\"\n"
- "androidboot.veritymode = \"enforcing\"\n"
- "androidboot.verifiedbootstate = \"orange\"\n"
- "androidboot.space = \"sha256 5248 androidboot.nospace = nope\"\n";
-
-const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
- {"androidboot.bootdevice", "1d84000.ufshc"},
- {"androidboot.boot_devices", "dev1, dev2,withcomma, dev3"},
- {"androidboot.baseband", "sdy"},
- {"androidboot.keymaster", "1"},
- {"androidboot.serialno", "BLAHBLAHBLAH"},
- {"androidboot.slot_suffix", "_a"},
- {"androidboot.hardware.platform", "sdw813"},
- {"androidboot.hardware", "foo"},
- {"androidboot.revision", "EVT1.0"},
- {"androidboot.bootloader", "burp-0.1-7521"},
- {"androidboot.hardware.sku", "mary"},
- {"androidboot.hardware.radio.subtype", "0"},
- {"androidboot.dtbo_idx", "2"},
- {"androidboot.mode", "normal"},
- {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
- {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
- {"androidboot.hardware.ufs", "2GB,combushi"},
- {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
- {"androidboot.ramdump", "disabled"},
- {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
- {"androidboot.vbmeta.avb_version", "1.1"},
- {"androidboot.vbmeta.device_state", "unlocked"},
- {"androidboot.vbmeta.hash_alg", "sha256"},
- {"androidboot.vbmeta.size", "5248"},
- {"androidboot.vbmeta.digest",
- "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
- {"androidboot.vbmeta.invalidate_on_error", "yes"},
- {"androidboot.veritymode", "enforcing"},
- {"androidboot.verifiedbootstate", "orange"},
- {"androidboot.space", "sha256 5248 androidboot.nospace = nope"},
-};
-
-bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
- // clang-format off
- return lhs.wait == rhs.wait &&
- lhs.check == rhs.check &&
- lhs.crypt == rhs.crypt &&
- lhs.nonremovable == rhs.nonremovable &&
- lhs.vold_managed == rhs.vold_managed &&
- lhs.recovery_only == rhs.recovery_only &&
- lhs.verify == rhs.verify &&
- lhs.force_crypt == rhs.force_crypt &&
- lhs.no_emulated_sd == rhs.no_emulated_sd &&
- lhs.no_trim == rhs.no_trim &&
- lhs.file_encryption == rhs.file_encryption &&
- lhs.formattable == rhs.formattable &&
- lhs.slot_select == rhs.slot_select &&
- lhs.force_fde_or_fbe == rhs.force_fde_or_fbe &&
- lhs.late_mount == rhs.late_mount &&
- lhs.no_fail == rhs.no_fail &&
- lhs.verify_at_boot == rhs.verify_at_boot &&
- lhs.quota == rhs.quota &&
- lhs.avb == rhs.avb &&
- lhs.logical == rhs.logical &&
- lhs.checkpoint_blk == rhs.checkpoint_blk &&
- lhs.checkpoint_fs == rhs.checkpoint_fs &&
- lhs.first_stage_mount == rhs.first_stage_mount &&
- lhs.slot_select_other == rhs.slot_select_other &&
- lhs.fs_verity == rhs.fs_verity;
- // clang-format on
-}
-
} // namespace
-TEST(fs_mgr, fs_mgr_parse_cmdline) {
- EXPECT_EQ(result_space, fs_mgr_parse_cmdline(cmdline));
+TEST(fs_mgr, fs_mgr_parse_boot_config) {
+ EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
}
TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
@@ -235,27 +140,6 @@ TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
EXPECT_TRUE(content.empty()) << content;
}
-TEST(fs_mgr, fs_mgr_parse_bootconfig) {
- EXPECT_EQ(bootconfig_result_space, fs_mgr_parse_proc_bootconfig(bootconfig));
-}
-
-TEST(fs_mgr, fs_mgr_get_boot_config_from_bootconfig) {
- std::string content;
- for (const auto& entry : bootconfig_result_space) {
- static constexpr char androidboot[] = "androidboot.";
- if (!android::base::StartsWith(entry.first, androidboot)) continue;
- auto key = entry.first.substr(strlen(androidboot));
- EXPECT_TRUE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, &content))
- << " for " << key;
- EXPECT_EQ(entry.second, content);
- }
-
- EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "vbmeta.avb_versio", &content));
- EXPECT_TRUE(content.empty()) << content;
- EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "nospace", &content));
- EXPECT_TRUE(content.empty()) << content;
-}
-
TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
Fstab fstab;
ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab));
@@ -321,18 +205,19 @@ TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
EXPECT_EQ(i, fstab.size());
}
-TEST(fs_mgr, ReadFstabFromFile_MountOptions) {
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_MountOptions) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source / ext4 ro,barrier=1 wait,avb
+source / ext4 ro,barrier=1 wait,slotselect,avb
source /metadata ext4 noatime,nosuid,nodev,discard wait,formattable
source /data f2fs noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
source /misc emmc defaults defaults
-source /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait
+source /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait,slotselect
source auto vfat defaults voldmanaged=usb:auto
source none swap defaults zramsize=1073741824,max_comp_streams=8
@@ -345,75 +230,94 @@ source none5 swap rw zramsize=1073741824,
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(11U, fstab.size());
-
- FstabEntry* entry = GetEntryForMountPoint(&fstab, "/");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), entry->flags);
- EXPECT_EQ("barrier=1", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "/metadata");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), entry->flags);
- EXPECT_EQ("discard", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "/data");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), entry->flags);
- EXPECT_EQ("discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "/misc");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(0U, entry->flags);
- EXPECT_EQ("", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "/vendor/firmware_mnt");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), entry->flags);
+ ASSERT_EQ(11U, fstab.size());
+
+ EXPECT_EQ("/", fstab[0].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[0].flags);
+ EXPECT_EQ("barrier=1", fstab[0].fs_options);
+
+ EXPECT_EQ("/metadata", fstab[1].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), fstab[1].flags);
+ EXPECT_EQ("discard", fstab[1].fs_options);
+
+ EXPECT_EQ("/data", fstab[2].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), fstab[2].flags);
+ EXPECT_EQ("discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier", fstab[2].fs_options);
+
+ EXPECT_EQ("/misc", fstab[3].mount_point);
+ EXPECT_EQ(0U, fstab[3].flags);
+ EXPECT_EQ("", fstab[3].fs_options);
+
+ EXPECT_EQ("/vendor/firmware_mnt", fstab[4].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[4].flags);
EXPECT_EQ(
"shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,"
"context=u:object_r:firmware_file:s0",
- entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "auto");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(0U, entry->flags);
- EXPECT_EQ("", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "none");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(0U, entry->flags);
- EXPECT_EQ("", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "none2");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_NODIRATIME | MS_REMOUNT | MS_BIND), entry->flags);
- EXPECT_EQ("", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "none3");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE), entry->flags);
- EXPECT_EQ("", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "none4");
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(static_cast<unsigned long>(MS_NOEXEC | MS_SHARED | MS_REC), entry->flags);
- EXPECT_EQ("", entry->fs_options);
-
- entry = GetEntryForMountPoint(&fstab, "none5");
- ASSERT_NE(nullptr, entry);
- // rw is the default.
- EXPECT_EQ(0U, entry->flags);
- EXPECT_EQ("", entry->fs_options);
+ fstab[4].fs_options);
+
+ EXPECT_EQ("auto", fstab[5].mount_point);
+ EXPECT_EQ(0U, fstab[5].flags);
+ EXPECT_EQ("", fstab[5].fs_options);
+
+ EXPECT_EQ("none", fstab[6].mount_point);
+ EXPECT_EQ(0U, fstab[6].flags);
+ EXPECT_EQ("", fstab[6].fs_options);
+
+ EXPECT_EQ("none2", fstab[7].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NODIRATIME | MS_REMOUNT | MS_BIND), fstab[7].flags);
+ EXPECT_EQ("", fstab[7].fs_options);
+
+ EXPECT_EQ("none3", fstab[8].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE), fstab[8].flags);
+ EXPECT_EQ("", fstab[8].fs_options);
+
+ EXPECT_EQ("none4", fstab[9].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NOEXEC | MS_SHARED | MS_REC), fstab[9].flags);
+ EXPECT_EQ("", fstab[9].fs_options);
+
+ EXPECT_EQ("none5", fstab[10].mount_point);
+ EXPECT_EQ(0U, fstab[10].flags); // rw is the same as defaults
+ EXPECT_EQ("", fstab[10].fs_options);
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrFlags) {
+static bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
+ // clang-format off
+ return lhs.wait == rhs.wait &&
+ lhs.check == rhs.check &&
+ lhs.crypt == rhs.crypt &&
+ lhs.nonremovable == rhs.nonremovable &&
+ lhs.vold_managed == rhs.vold_managed &&
+ lhs.recovery_only == rhs.recovery_only &&
+ lhs.verify == rhs.verify &&
+ lhs.force_crypt == rhs.force_crypt &&
+ lhs.no_emulated_sd == rhs.no_emulated_sd &&
+ lhs.no_trim == rhs.no_trim &&
+ lhs.file_encryption == rhs.file_encryption &&
+ lhs.formattable == rhs.formattable &&
+ lhs.slot_select == rhs.slot_select &&
+ lhs.force_fde_or_fbe == rhs.force_fde_or_fbe &&
+ lhs.late_mount == rhs.late_mount &&
+ lhs.no_fail == rhs.no_fail &&
+ lhs.verify_at_boot == rhs.verify_at_boot &&
+ lhs.quota == rhs.quota &&
+ lhs.avb == rhs.avb &&
+ lhs.logical == rhs.logical &&
+ lhs.checkpoint_blk == rhs.checkpoint_blk &&
+ lhs.checkpoint_fs == rhs.checkpoint_fs &&
+ lhs.first_stage_mount == rhs.first_stage_mount &&
+ lhs.slot_select_other == rhs.slot_select_other &&
+ lhs.fs_verity == rhs.fs_verity;
+ // clang-format on
+}
+
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_FsMgrFlags) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
source none0 swap defaults wait,check,nonremovable,recoveryonly,verifyatboot,verify
-source none1 swap defaults avb,noemulatedsd,notrim,formattable,nofail
-source none2 swap defaults first_stage_mount,latemount,quota,logical
+source none1 swap defaults avb,noemulatedsd,notrim,formattable,slotselect,nofail
+source none2 swap defaults first_stage_mount,latemount,quota,logical,slotselect_other
source none3 swap defaults checkpoint=block
source none4 swap defaults checkpoint=fs
source none5 swap defaults defaults
@@ -422,10 +326,10 @@ source none5 swap defaults defaults
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(6U, fstab.size());
+ ASSERT_EQ(6U, fstab.size());
- FstabEntry* entry = GetEntryForMountPoint(&fstab, "none0");
- ASSERT_NE(nullptr, entry);
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.wait = true;
@@ -436,48 +340,50 @@ source none5 swap defaults defaults
flags.verify = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
+ entry++;
- entry = GetEntryForMountPoint(&fstab, "none1");
- ASSERT_NE(nullptr, entry);
+ EXPECT_EQ("none1", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.avb = true;
flags.no_emulated_sd = true;
flags.no_trim = true;
flags.formattable = true;
+ flags.slot_select = true;
flags.no_fail = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
+ entry++;
- entry = GetEntryForMountPoint(&fstab, "none2");
- ASSERT_NE(nullptr, entry);
+ EXPECT_EQ("none2", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.first_stage_mount = true;
flags.late_mount = true;
flags.quota = true;
flags.logical = true;
+ flags.slot_select_other = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
+ entry++;
- entry = GetEntryForMountPoint(&fstab, "none3");
- ASSERT_NE(nullptr, entry);
+ EXPECT_EQ("none3", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.checkpoint_blk = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
+ entry++;
- entry = GetEntryForMountPoint(&fstab, "none4");
- ASSERT_NE(nullptr, entry);
+ EXPECT_EQ("none4", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.checkpoint_fs = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
+ entry++;
- entry = GetEntryForMountPoint(&fstab, "none5");
- ASSERT_NE(nullptr, entry);
+ EXPECT_EQ("none5", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
@@ -499,7 +405,7 @@ source none2 swap defaults forcefdeorfbe=
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(3U, fstab.size());
+ ASSERT_EQ(3U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -569,7 +475,7 @@ source none0 swap defaults encryptable=/dir/key
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
flags.crypt = true;
@@ -593,7 +499,7 @@ source none3 swap defaults voldmanaged=sdcard:auto
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(4U, fstab.size());
+ ASSERT_EQ(4U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
flags.vold_managed = true;
@@ -634,7 +540,7 @@ source none1 swap defaults length=123456
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(2U, fstab.size());
+ ASSERT_EQ(2U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -660,7 +566,7 @@ source none1 swap defaults swapprio=123456
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(2U, fstab.size());
+ ASSERT_EQ(2U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -690,7 +596,7 @@ source none5 swap defaults zramsize=%
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(6U, fstab.size());
+ ASSERT_EQ(6U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -736,7 +642,7 @@ source none0 swap defaults forceencrypt=/dir/key
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -759,7 +665,7 @@ source none0 swap defaults forcefdeorfbe=/dir/key
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -783,7 +689,7 @@ source none0 swap defaults fileencryption=aes-256-xts:aes-256-cts:v
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
flags.file_encryption = true;
@@ -805,7 +711,7 @@ source none1 swap defaults max_comp_streams=123456
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(2U, fstab.size());
+ ASSERT_EQ(2U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -833,7 +739,7 @@ source none3 swap defaults reservedsize=2m
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(4U, fstab.size());
+ ASSERT_EQ(4U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -871,7 +777,7 @@ source none3 swap defaults eraseblk=8192
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(4U, fstab.size());
+ ASSERT_EQ(4U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -909,7 +815,7 @@ source none3 swap defaults logicalblk=8192
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(4U, fstab.size());
+ ASSERT_EQ(4U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
@@ -946,7 +852,7 @@ source none1 swap defaults avb_keys=/path/to/test.avbpubkey
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(2U, fstab.size());
+ ASSERT_EQ(2U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -975,7 +881,7 @@ source none0 swap defaults keydirectory=/dir/key
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -997,7 +903,7 @@ source none0 swap defaults keydirectory=/dir/key,metadata_encryptio
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("adiantum", entry->metadata_encryption);
@@ -1014,7 +920,7 @@ source none0 swap defaults keydirectory=/dir/key,metadata_encryptio
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
@@ -1035,7 +941,7 @@ source none0 swap defaults sysfs_path=/sys/device
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(1U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
@@ -1061,7 +967,7 @@ source none4 swap defaults zram_backingdev_size=2m
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(4U, fstab.size());
+ ASSERT_EQ(4U, fstab.size());
auto entry = fstab.begin();
@@ -1105,59 +1011,3 @@ TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, block_device))
<< "/data wasn't mounted from default fstab";
}
-
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- std::string fstab_contents = R"fs(
-source none0 swap defaults readahead_size_kb=blah
-source none1 swap defaults readahead_size_kb=128
-source none2 swap defaults readahead_size_kb=5%
-source none3 swap defaults readahead_size_kb=5kb
-source none4 swap defaults readahead_size_kb=16385
-source none5 swap defaults readahead_size_kb=-128
-source none6 swap defaults readahead_size_kb=0
-)fs";
- ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
- Fstab fstab;
- EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_LE(7U, fstab.size());
-
- FstabEntry::FsMgrFlags flags = {};
-
- auto entry = fstab.begin();
- EXPECT_EQ("none0", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(-1, entry->readahead_size_kb);
- entry++;
-
- EXPECT_EQ("none1", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(128, entry->readahead_size_kb);
- entry++;
-
- EXPECT_EQ("none2", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(-1, entry->readahead_size_kb);
- entry++;
-
- EXPECT_EQ("none3", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(-1, entry->readahead_size_kb);
- entry++;
-
- EXPECT_EQ("none4", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(-1, entry->readahead_size_kb);
- entry++;
-
- EXPECT_EQ("none5", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(-1, entry->readahead_size_kb);
- entry++;
-
- EXPECT_EQ("none6", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ(0, entry->readahead_size_kb);
-}
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
index 462777d13..4d4aae41a 100644
--- a/fs_mgr/tools/Android.bp
+++ b/fs_mgr/tools/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "dmctl",
srcs: ["dmctl.cpp"],
@@ -33,15 +29,3 @@ cc_binary {
cflags: ["-Werror"],
}
-
-cc_binary {
- name: "dmuserd",
- srcs: ["dmuserd.cpp"],
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- cflags: ["-Werror"],
-}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 62ca1620b..27384571a 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,7 +38,6 @@
#include <vector>
using namespace std::literals::string_literals;
-using namespace std::chrono_literals;
using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
@@ -50,7 +49,6 @@ static int Usage(void) {
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
- std::cerr << " getuuid <dm-name>" << std::endl;
std::cerr << " info <dm-name>" << std::endl;
std::cerr << " status <dm-name>" << std::endl;
std::cerr << " resume <dm-name>" << std::endl;
@@ -176,13 +174,6 @@ class TargetParser final {
}
return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
cow_device, mode, chunk_size);
- } else if (target_type == "user") {
- if (!HasArgs(1)) {
- std::cerr << "Expected \"user\" <control_device_name>" << std::endl;
- return nullptr;
- }
- std::string control_device = NextArg();
- return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
@@ -248,9 +239,8 @@ static int DmCreateCmdHandler(int argc, char** argv) {
return ret;
}
- std::string ignore_path;
DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
+ if (!dm.CreateDevice(name, table)) {
std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
return -EIO;
}
@@ -399,22 +389,6 @@ static int GetPathCmdHandler(int argc, char** argv) {
return 0;
}
-static int GetUuidCmdHandler(int argc, char** argv) {
- if (argc != 1) {
- std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
- return -EINVAL;
- }
-
- DeviceMapper& dm = DeviceMapper::Instance();
- std::string uuid;
- if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
- std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
- return -EINVAL;
- }
- std::cout << uuid << std::endl;
- return 0;
-}
-
static int InfoCmdHandler(int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -528,7 +502,6 @@ static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
{"list", DmListCmdHandler},
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
- {"getuuid", GetUuidCmdHandler},
{"info", InfoCmdHandler},
{"table", TableCmdHandler},
{"status", StatusCmdHandler},
diff --git a/fs_mgr/tools/dmuserd.cpp b/fs_mgr/tools/dmuserd.cpp
deleted file mode 100644
index 6b68b2871..000000000
--- a/fs_mgr/tools/dmuserd.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-#define _LARGEFILE64_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <unistd.h>
-#include <iostream>
-
-#define SECTOR_SIZE ((__u64)512)
-#define BUFFER_BYTES 4096
-
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-
-/* This should be replaced with linux/dm-user.h. */
-#ifndef _LINUX_DM_USER_H
-#define _LINUX_DM_USER_H
-
-#include <linux/types.h>
-
-#define DM_USER_REQ_MAP_READ 0
-#define DM_USER_REQ_MAP_WRITE 1
-#define DM_USER_REQ_MAP_FLUSH 2
-#define DM_USER_REQ_MAP_DISCARD 3
-#define DM_USER_REQ_MAP_SECURE_ERASE 4
-#define DM_USER_REQ_MAP_WRITE_SAME 5
-#define DM_USER_REQ_MAP_WRITE_ZEROES 6
-#define DM_USER_REQ_MAP_ZONE_OPEN 7
-#define DM_USER_REQ_MAP_ZONE_CLOSE 8
-#define DM_USER_REQ_MAP_ZONE_FINISH 9
-#define DM_USER_REQ_MAP_ZONE_APPEND 10
-#define DM_USER_REQ_MAP_ZONE_RESET 11
-#define DM_USER_REQ_MAP_ZONE_RESET_ALL 12
-
-#define DM_USER_REQ_MAP_FLAG_FAILFAST_DEV 0x00001
-#define DM_USER_REQ_MAP_FLAG_FAILFAST_TRANSPORT 0x00002
-#define DM_USER_REQ_MAP_FLAG_FAILFAST_DRIVER 0x00004
-#define DM_USER_REQ_MAP_FLAG_SYNC 0x00008
-#define DM_USER_REQ_MAP_FLAG_META 0x00010
-#define DM_USER_REQ_MAP_FLAG_PRIO 0x00020
-#define DM_USER_REQ_MAP_FLAG_NOMERGE 0x00040
-#define DM_USER_REQ_MAP_FLAG_IDLE 0x00080
-#define DM_USER_REQ_MAP_FLAG_INTEGRITY 0x00100
-#define DM_USER_REQ_MAP_FLAG_FUA 0x00200
-#define DM_USER_REQ_MAP_FLAG_PREFLUSH 0x00400
-#define DM_USER_REQ_MAP_FLAG_RAHEAD 0x00800
-#define DM_USER_REQ_MAP_FLAG_BACKGROUND 0x01000
-#define DM_USER_REQ_MAP_FLAG_NOWAIT 0x02000
-#define DM_USER_REQ_MAP_FLAG_CGROUP_PUNT 0x04000
-#define DM_USER_REQ_MAP_FLAG_NOUNMAP 0x08000
-#define DM_USER_REQ_MAP_FLAG_HIPRI 0x10000
-#define DM_USER_REQ_MAP_FLAG_DRV 0x20000
-#define DM_USER_REQ_MAP_FLAG_SWAP 0x40000
-
-#define DM_USER_RESP_SUCCESS 0
-#define DM_USER_RESP_ERROR 1
-#define DM_USER_RESP_UNSUPPORTED 2
-
-struct dm_user_message {
- __u64 seq;
- __u64 type;
- __u64 flags;
- __u64 sector;
- __u64 len;
- __u8 buf[];
-};
-
-#endif
-
-static bool verbose = false;
-
-ssize_t write_all(int fd, void* buf, size_t len) {
- char* buf_c = (char*)buf;
- ssize_t total = 0;
- ssize_t once;
-
- while (total < static_cast<ssize_t>(len)) {
- once = write(fd, buf_c + total, len - total);
- if (once < 0) return once;
- if (once == 0) {
- errno = ENOSPC;
- return 0;
- }
- total += once;
- }
-
- return total;
-}
-
-ssize_t read_all(int fd, void* buf, size_t len) {
- char* buf_c = (char*)buf;
- ssize_t total = 0;
- ssize_t once;
-
- while (total < static_cast<ssize_t>(len)) {
- once = read(fd, buf_c + total, len - total);
- if (once < 0) return once;
- if (once == 0) {
- errno = ENOSPC;
- return 0;
- }
- total += once;
- }
-
- return total;
-}
-
-int not_splice(int from, int to, __u64 count) {
- while (count > 0) {
- char buf[BUFFER_BYTES];
- __u64 max = count > BUFFER_BYTES ? BUFFER_BYTES : count;
-
- if (read_all(from, buf, max) <= 0) {
- perror("Unable to read");
- return -EIO;
- }
-
- if (write_all(to, buf, max) <= 0) {
- perror("Unable to write");
- return -EIO;
- }
-
- count -= max;
- }
-
- return 0;
-}
-
-int simple_daemon(char* control_path, char* backing_path) {
- int control_fd = open(control_path, O_RDWR);
- if (control_fd < 0) {
- fprintf(stderr, "Unable to open control device %s\n", control_path);
- return -1;
- }
-
- int backing_fd = open(backing_path, O_RDWR);
- if (backing_fd < 0) {
- fprintf(stderr, "Unable to open backing device %s\n", backing_path);
- return -1;
- }
-
- while (1) {
- struct dm_user_message msg;
- char* base;
- __u64 type;
-
- if (verbose) std::cerr << "dmuserd: Waiting for message...\n";
-
- if (read_all(control_fd, &msg, sizeof(msg)) < 0) {
- if (errno == ENOTBLK) return 0;
-
- perror("unable to read msg");
- return -1;
- }
-
- if (verbose) {
- std::string type;
- switch (msg.type) {
- case DM_USER_REQ_MAP_WRITE:
- type = "write";
- break;
- case DM_USER_REQ_MAP_READ:
- type = "read";
- break;
- case DM_USER_REQ_MAP_FLUSH:
- type = "flush";
- break;
- default:
- /*
- * FIXME: Can't I do "whatever"s here rather that
- * std::string("whatever")?
- */
- type = std::string("(unknown, id=") + std::to_string(msg.type) + ")";
- break;
- }
-
- std::string flags;
- if (msg.flags & DM_USER_REQ_MAP_FLAG_SYNC) {
- if (!flags.empty()) flags += "|";
- flags += "S";
- }
- if (msg.flags & DM_USER_REQ_MAP_FLAG_META) {
- if (!flags.empty()) flags += "|";
- flags += "M";
- }
- if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
- if (!flags.empty()) flags += "|";
- flags += "FUA";
- }
- if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH) {
- if (!flags.empty()) flags += "|";
- flags += "F";
- }
-
- std::cerr << "dmuserd: Got " << type << " request " << flags << " for sector "
- << std::to_string(msg.sector) << " with length " << std::to_string(msg.len)
- << "\n";
- }
-
- type = msg.type;
- switch (type) {
- case DM_USER_REQ_MAP_READ:
- msg.type = DM_USER_RESP_SUCCESS;
- break;
- case DM_USER_REQ_MAP_WRITE:
- if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH ||
- msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
- if (fsync(backing_fd) < 0) {
- perror("Unable to fsync(), just sync()ing instead");
- sync();
- }
- }
- msg.type = DM_USER_RESP_SUCCESS;
- if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {
- perror("Unable to seek");
- return -1;
- }
- if (not_splice(control_fd, backing_fd, msg.len) < 0) {
- if (errno == ENOTBLK) return 0;
- std::cerr << "unable to handle write data\n";
- return -1;
- }
- if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
- if (fsync(backing_fd) < 0) {
- perror("Unable to fsync(), just sync()ing instead");
- sync();
- }
- }
- break;
- case DM_USER_REQ_MAP_FLUSH:
- msg.type = DM_USER_RESP_SUCCESS;
- if (fsync(backing_fd) < 0) {
- perror("Unable to fsync(), just sync()ing instead");
- sync();
- }
- break;
- default:
- std::cerr << "dmuserd: unsupported op " << std::to_string(msg.type) << "\n";
- msg.type = DM_USER_RESP_UNSUPPORTED;
- break;
- }
-
- if (verbose) std::cerr << "dmuserd: Responding to message\n";
-
- if (write_all(control_fd, &msg, sizeof(msg)) < 0) {
- if (errno == ENOTBLK) return 0;
- perror("unable to write msg");
- return -1;
- }
-
- switch (type) {
- case DM_USER_REQ_MAP_READ:
- if (verbose) std::cerr << "dmuserd: Sending read data\n";
- if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {
- perror("Unable to seek");
- return -1;
- }
- if (not_splice(backing_fd, control_fd, msg.len) < 0) {
- if (errno == ENOTBLK) return 0;
- std::cerr << "unable to handle read data\n";
- return -1;
- }
- break;
- }
- }
-
- /* The daemon doesn't actully terminate for this test. */
- perror("Unable to read from control device");
- return -1;
-}
-
-void usage(char* prog) {
- printf("Usage: %s\n", prog);
- printf(" Handles block requests in userspace, backed by memory\n");
- printf(" -h Display this help message\n");
- printf(" -c <control dev> Control device to use for the test\n");
- printf(" -b <store path> The file to use as a backing store, otherwise memory\n");
- printf(" -v Enable verbose mode\n");
-}
-
-int main(int argc, char* argv[]) {
- char* control_path = NULL;
- char* backing_path = NULL;
- char* store;
- int c;
-
- prctl(PR_SET_IO_FLUSHER, 0, 0, 0, 0);
-
- while ((c = getopt(argc, argv, "h:c:s:b:v")) != -1) {
- switch (c) {
- case 'h':
- usage(basename(argv[0]));
- exit(0);
- case 'c':
- control_path = strdup(optarg);
- break;
- case 'b':
- backing_path = strdup(optarg);
- break;
- case 'v':
- verbose = true;
- break;
- default:
- usage(basename(argv[0]));
- exit(1);
- }
- }
-
- int r = simple_daemon(control_path, backing_path);
- if (r) fprintf(stderr, "simple_daemon() errored out\n");
- return r;
-}
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 95e814b6a..27a645288 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "gatekeeperd",
cflags: [
@@ -32,7 +28,6 @@ cc_binary {
shared_libs: [
"libbinder",
- "libbinder_ndk",
"libgatekeeper",
"libgsi",
"liblog",
@@ -40,11 +35,11 @@ cc_binary {
"libbase",
"libutils",
"libcrypto",
+ "libkeystore_aidl",
+ "libkeystore_binder",
"libhidlbase",
"android.hardware.gatekeeper@1.0",
"libgatekeeper_aidl",
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.security.authorization-ndk_platform",
],
static_libs: ["libscrypt_static"],
diff --git a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
index 67e86339a..57adaba07 100644
--- a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
+++ b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
@@ -27,11 +27,10 @@ import android.service.gatekeeper.GateKeeperResponse;
*
* @hide
*/
-@SensitiveData
interface IGateKeeperService {
/**
* Enrolls a password, returning the handle to the enrollment to be stored locally.
- * @param userId The Android user ID associated to this enrollment
+ * @param uid The Android user ID associated to this enrollment
* @param currentPasswordHandle The previously enrolled handle, or null if none
* @param currentPassword The previously enrolled plaintext password, or null if none.
* If provided, must verify against the currentPasswordHandle.
@@ -39,22 +38,22 @@ interface IGateKeeperService {
* upon success.
* @return an EnrollResponse or null on failure
*/
- GateKeeperResponse enroll(int userId, in @nullable byte[] currentPasswordHandle,
+ GateKeeperResponse enroll(int uid, in @nullable byte[] currentPasswordHandle,
in @nullable byte[] currentPassword, in byte[] desiredPassword);
/**
* Verifies an enrolled handle against a provided, plaintext blob.
- * @param userId The Android user ID associated to this enrollment
+ * @param uid The Android user ID associated to this enrollment
* @param enrolledPasswordHandle The handle against which the provided password will be
* verified.
* @param The plaintext blob to verify against enrolledPassword.
* @return a VerifyResponse, or null on failure.
*/
- GateKeeperResponse verify(int userId, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
+ GateKeeperResponse verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
/**
* Verifies an enrolled handle against a provided, plaintext blob.
- * @param userId The Android user ID associated to this enrollment
+ * @param uid The Android user ID associated to this enrollment
* @param challenge a challenge to authenticate agaisnt the device credential. If successful
* authentication occurs, this value will be written to the returned
* authentication attestation.
@@ -63,22 +62,22 @@ interface IGateKeeperService {
* @param The plaintext blob to verify against enrolledPassword.
* @return a VerifyResponse with an attestation, or null on failure.
*/
- GateKeeperResponse verifyChallenge(int userId, long challenge, in byte[] enrolledPasswordHandle,
+ GateKeeperResponse verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
in byte[] providedPassword);
/**
* Retrieves the secure identifier for the user with the provided Android ID,
* or 0 if none is found.
- * @param userId the Android user id
+ * @param uid the Android user id
*/
- long getSecureUserId(int userId);
+ long getSecureUserId(int uid);
/**
* Clears secure user id associated with the provided Android ID.
* Must be called when password is set to NONE.
- * @param userId the Android user id.
+ * @param uid the Android user id.
*/
- void clearSecureUserId(int userId);
+ void clearSecureUserId(int uid);
/**
* Notifies gatekeeper that device setup has been completed and any potentially still existing
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 8792c8352..1d65b1ced 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -19,43 +19,42 @@
#include <android/service/gatekeeper/BnGateKeeperService.h>
#include <gatekeeper/GateKeeperResponse.h>
-#include <endian.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
#include <unistd.h>
#include <memory>
+#include <android/security/keystore/IKeystoreService.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_manager.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
-#include <gatekeeper/password_handle.h> // for password_handle_t
+#include <gatekeeper/password_handle.h> // for password_handle_t
+#include <hardware/gatekeeper.h>
#include <hardware/hw_auth_token.h>
+#include <keystore/keystore.h> // For error code
+#include <keystore/keystore_return_types.h>
#include <libgsi/libgsi.h>
#include <log/log.h>
+#include <utils/Log.h>
#include <utils/String16.h>
-#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
-#include <aidl/android/security/authorization/IKeystoreAuthorization.h>
-#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
#include <hidl/HidlSupport.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
using android::sp;
-using android::hardware::Return;
-using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
-using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
+using android::hardware::Return;
using ::android::binder::Status;
using ::android::service::gatekeeper::BnGateKeeperService;
using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
-using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;
-using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
-using ::aidl::android::security::authorization::IKeystoreAuthorization;
namespace android {
@@ -63,7 +62,7 @@ static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SE
static const String16 DUMP_PERMISSION("android.permission.DUMP");
class GateKeeperProxy : public BnGateKeeperService {
- public:
+public:
GateKeeperProxy() {
clear_state_if_needed_done = false;
hw_device = IGatekeeper::getService();
@@ -74,11 +73,12 @@ class GateKeeperProxy : public BnGateKeeperService {
}
}
- virtual ~GateKeeperProxy() {}
+ virtual ~GateKeeperProxy() {
+ }
- void store_sid(uint32_t userId, uint64_t sid) {
+ void store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
- snprintf(filename, sizeof(filename), "%u", userId);
+ snprintf(filename, sizeof(filename), "%u", uid);
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
ALOGE("could not open file: %s: %s", filename, strerror(errno));
@@ -96,7 +96,7 @@ class GateKeeperProxy : public BnGateKeeperService {
if (mark_cold_boot() && !is_running_gsi) {
ALOGI("cold boot: clearing state");
if (hw_device) {
- hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
+ hw_device->deleteAllUsers([](const GatekeeperResponse &){});
}
}
@@ -104,7 +104,7 @@ class GateKeeperProxy : public BnGateKeeperService {
}
bool mark_cold_boot() {
- const char* filename = ".coldboot";
+ const char *filename = ".coldboot";
if (access(filename, F_OK) == -1) {
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
@@ -117,18 +117,18 @@ class GateKeeperProxy : public BnGateKeeperService {
return false;
}
- void maybe_store_sid(uint32_t userId, uint64_t sid) {
+ void maybe_store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
- snprintf(filename, sizeof(filename), "%u", userId);
+ snprintf(filename, sizeof(filename), "%u", uid);
if (access(filename, F_OK) == -1) {
- store_sid(userId, sid);
+ store_sid(uid, sid);
}
}
- uint64_t read_sid(uint32_t userId) {
+ uint64_t read_sid(uint32_t uid) {
char filename[21];
uint64_t sid;
- snprintf(filename, sizeof(filename), "%u", userId);
+ snprintf(filename, sizeof(filename), "%u", uid);
int fd = open(filename, O_RDONLY);
if (fd < 0) return 0;
read(fd, &sid, sizeof(sid));
@@ -136,31 +136,31 @@ class GateKeeperProxy : public BnGateKeeperService {
return sid;
}
- void clear_sid(uint32_t userId) {
+ void clear_sid(uint32_t uid) {
char filename[21];
- snprintf(filename, sizeof(filename), "%u", userId);
+ snprintf(filename, sizeof(filename), "%u", uid);
if (remove(filename) < 0) {
ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
- store_sid(userId, 0);
+ store_sid(uid, 0);
}
}
- // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
+ // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
// secure storage shared across a GSI image and a host image will not overlap.
- uint32_t adjust_userId(uint32_t userId) {
+ uint32_t adjust_uid(uint32_t uid) {
static constexpr uint32_t kGsiOffset = 1000000;
- CHECK(userId < kGsiOffset);
+ CHECK(uid < kGsiOffset);
CHECK(hw_device != nullptr);
if (is_running_gsi) {
- return userId + kGsiOffset;
+ return uid + kGsiOffset;
}
- return userId;
+ return uid;
}
#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
- Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
- const std::optional<std::vector<uint8_t>>& currentPassword,
+ Status enroll(int32_t uid, const std::unique_ptr<std::vector<uint8_t>>& currentPasswordHandle,
+ const std::unique_ptr<std::vector<uint8_t>>& currentPassword,
const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
@@ -198,10 +198,9 @@ class GateKeeperProxy : public BnGateKeeperService {
android::hardware::hidl_vec<uint8_t> newPwd;
newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
- uint32_t hw_userId = adjust_userId(userId);
+ uint32_t hw_uid = adjust_uid(uid);
Return<void> hwRes = hw_device->enroll(
- hw_userId, curPwdHandle, curPwd, newPwd,
- [&gkResponse](const GatekeeperResponse& rsp) {
+ hw_uid, curPwdHandle, curPwd, newPwd, [&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
*gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
} else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
@@ -226,12 +225,12 @@ class GateKeeperProxy : public BnGateKeeperService {
const gatekeeper::password_handle_t* handle =
reinterpret_cast<const gatekeeper::password_handle_t*>(
gkResponse->payload().data());
- store_sid(userId, handle->user_id);
+ store_sid(uid, handle->user_id);
GKResponse verifyResponse;
// immediately verify this password so we don't ask the user to enter it again
// if they just created it.
- auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);
+ auto status = verify(uid, gkResponse->payload(), desiredPassword, &verifyResponse);
if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
LOG(ERROR) << "Failed to verify password after enrolling";
}
@@ -240,13 +239,13 @@ class GateKeeperProxy : public BnGateKeeperService {
return Status::ok();
}
- Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+ Status verify(int32_t uid, const ::std::vector<uint8_t>& enrolledPasswordHandle,
const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
- return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+ return verifyChallenge(uid, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
gkResponse);
}
- Status verifyChallenge(int32_t userId, int64_t challenge,
+ Status verifyChallenge(int32_t uid, int64_t challenge,
const std::vector<uint8_t>& enrolledPasswordHandle,
const std::vector<uint8_t>& providedPassword,
GKResponse* gkResponse) override {
@@ -270,7 +269,7 @@ class GateKeeperProxy : public BnGateKeeperService {
reinterpret_cast<const gatekeeper::password_handle_t*>(
enrolledPasswordHandle.data());
- uint32_t hw_userId = adjust_userId(userId);
+ uint32_t hw_uid = adjust_uid(uid);
android::hardware::hidl_vec<uint8_t> curPwdHandle;
curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
enrolledPasswordHandle.size());
@@ -279,7 +278,7 @@ class GateKeeperProxy : public BnGateKeeperService {
providedPassword.size());
Return<void> hwRes = hw_device->verify(
- hw_userId, challenge, curPwdHandle, enteredPwd,
+ hw_uid, challenge, curPwdHandle, enteredPwd,
[&gkResponse](const GatekeeperResponse& rsp) {
if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
*gkResponse = GKResponse::ok(
@@ -299,52 +298,35 @@ class GateKeeperProxy : public BnGateKeeperService {
if (gkResponse->response_code() == GKResponseCode::OK) {
if (gkResponse->payload().size() != 0) {
- // try to connect to IKeystoreAuthorization AIDL service first.
- AIBinder* authzAIBinder =
- AServiceManager_getService("android.security.authorization");
- ::ndk::SpAIBinder authzBinder(authzAIBinder);
- auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
- if (authzService) {
- if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {
- LOG(ERROR) << "Incorrect size of AuthToken payload.";
- return GK_ERROR;
- }
-
- const hw_auth_token_t* hwAuthToken =
- reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());
- HardwareAuthToken authToken;
-
- authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
- authToken.challenge = hwAuthToken->challenge;
- authToken.userId = hwAuthToken->user_id;
- authToken.authenticatorId = hwAuthToken->authenticator_id;
- authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
- betoh32(hwAuthToken->authenticator_type));
- authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);
- auto result = authzService->addAuthToken(authToken);
- if (!result.isOk()) {
- LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
- return GK_ERROR;
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<security::keystore::IKeystoreService> service =
+ interface_cast<security::keystore::IKeystoreService>(binder);
+
+ if (service) {
+ int result = 0;
+ auto binder_result = service->addAuthToken(gkResponse->payload(), &result);
+ if (!binder_result.isOk() ||
+ !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+ LOG(ERROR) << "Failure sending auth token to KeyStore: " << result;
}
} else {
- LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
- "Keystore.";
- return GK_ERROR;
+ LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with Keystore.";
}
}
- maybe_store_sid(userId, handle->user_id);
+ maybe_store_sid(uid, handle->user_id);
}
return Status::ok();
}
- Status getSecureUserId(int32_t userId, int64_t* sid) override {
- *sid = read_sid(userId);
+ Status getSecureUserId(int32_t uid, int64_t* sid) override {
+ *sid = read_sid(uid);
return Status::ok();
}
- Status clearSecureUserId(int32_t userId) override {
+ Status clearSecureUserId(int32_t uid) override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
@@ -352,11 +334,11 @@ class GateKeeperProxy : public BnGateKeeperService {
ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
return Status::ok();
}
- clear_sid(userId);
+ clear_sid(uid);
if (hw_device) {
- uint32_t hw_userId = adjust_userId(userId);
- hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
+ uint32_t hw_uid = adjust_uid(uid);
+ hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
}
return Status::ok();
}
@@ -383,23 +365,23 @@ class GateKeeperProxy : public BnGateKeeperService {
}
if (hw_device == NULL) {
- const char* result = "Device not available";
+ const char *result = "Device not available";
write(fd, result, strlen(result) + 1);
} else {
- const char* result = "OK";
+ const char *result = "OK";
write(fd, result, strlen(result) + 1);
}
return OK;
}
- private:
+private:
sp<IGatekeeper> hw_device;
bool clear_state_if_needed_done;
bool is_running_gsi;
};
-} // namespace android
+}// namespace android
int main(int argc, char* argv[]) {
ALOGI("Starting gatekeeperd...");
diff --git a/healthd/Android.bp b/healthd/Android.bp
index ec47f68b3..14d46b3d3 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libhealthd_headers",
vendor_available: true,
@@ -66,6 +62,29 @@ cc_binary {
srcs: [
"HealthServiceDefault.cpp",
],
+
+ overrides: [
+ "healthd",
+ ]
+}
+
+cc_binary {
+ name: "healthd",
+ defaults: ["android.hardware.health@2.0-service_defaults"],
+
+ init_rc: ["healthd.rc"],
+ srcs: [
+ "HealthServiceHealthd.cpp",
+ ],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.health@1.0",
+ ],
+
+ vintf_fragments: [
+ "manifest_healthd.xml"
+ ],
}
cc_library_static {
@@ -221,47 +240,3 @@ cc_test {
defaults: ["charger_defaults"],
srcs: ["charger_test.cpp"],
}
-
-cc_test {
- name: "libhealthd_charger_test",
- defaults: ["charger_defaults"],
- srcs: [
- "AnimationParser_test.cpp",
- "healthd_mode_charger_test.cpp"
- ],
- static_libs: [
- "libgmock",
- ],
- test_suites: [
- "general-tests",
- "device-tests",
- ],
- data: [
- ":libhealthd_charger_test_data",
- ],
- require_root: true,
-}
-
-// /system/etc/res/images/charger/battery_fail.png
-prebuilt_etc {
- name: "system_core_charger_res_images_battery_fail.png",
- src: "images/battery_fail.png",
- relative_install_path: "res/images/charger",
- filename: "battery_fail.png",
-}
-
-// /system/etc/res/images/charger/battery_scale.png
-prebuilt_etc {
- name: "system_core_charger_res_images_battery_scale.png",
- src: "images/battery_scale.png",
- relative_install_path: "res/images/charger",
- filename: "battery_scale.png",
-}
-
-phony {
- name: "charger_res_images",
- required: [
- "system_core_charger_res_images_battery_fail.png",
- "system_core_charger_res_images_battery_scale.png",
- ],
-}
diff --git a/healthd/Android.mk b/healthd/Android.mk
new file mode 100644
index 000000000..4b09cf826
--- /dev/null
+++ b/healthd/Android.mk
@@ -0,0 +1,36 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
+LOCAL_CHARGER_NO_UI := true
+endif
+
+### charger_res_images ###
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+define _add-charger-image
+include $$(CLEAR_VARS)
+LOCAL_MODULE := system_core_charger_res_images_$(notdir $(1))
+LOCAL_MODULE_STEM := $(notdir $(1))
+_img_modules += $$(LOCAL_MODULE)
+LOCAL_SRC_FILES := $1
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
+include $$(BUILD_PREBUILT)
+endef
+
+_img_modules :=
+_images :=
+$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
+ $(eval $(call _add-charger-image,$(_img))))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_res_images
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := $(_img_modules)
+include $(BUILD_PHONY_PACKAGE)
+
+_add-charger-image :=
+_img_modules :=
+endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index 6b08570d9..fde3b95e0 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -37,8 +37,8 @@ bool can_ignore_line(const char* str) {
return true;
}
-bool remove_prefix(std::string_view line, const char* prefix, const char** rest) {
- const char* str = line.data();
+bool remove_prefix(const std::string& line, const char* prefix, const char** rest) {
+ const char* str = line.c_str();
int start;
char c;
diff --git a/healthd/AnimationParser.h b/healthd/AnimationParser.h
index f55b5635e..bc0084518 100644
--- a/healthd/AnimationParser.h
+++ b/healthd/AnimationParser.h
@@ -17,8 +17,6 @@
#ifndef HEALTHD_ANIMATION_PARSER_H
#define HEALTHD_ANIMATION_PARSER_H
-#include <string_view>
-
#include "animation.h"
namespace android {
@@ -26,7 +24,7 @@ namespace android {
bool parse_animation_desc(const std::string& content, animation* anim);
bool can_ignore_line(const char* str);
-bool remove_prefix(std::string_view str, const char* prefix, const char** rest);
+bool remove_prefix(const std::string& str, const char* prefix, const char** rest);
bool parse_text_field(const char* in, animation::text_field* field);
} // namespace android
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 377acb75e..599f5005f 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -79,7 +79,7 @@ static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
// HIDL enum values are zero initialized, so they need to be initialized
// properly.
- health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED;
+ health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
health_info_2_1->batteryChargeTimeToFullNowSeconds =
(int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
auto* props = &health_info_2_1->legacy.legacy;
@@ -349,14 +349,9 @@ void BatteryMonitor::updateValues(void) {
}
void BatteryMonitor::logValues(void) {
- logValues(*mHealthInfo, *mHealthdConfig);
-}
-
-void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
- const struct healthd_config& healthd_config) {
char dmesgline[256];
size_t len;
- const HealthInfo_1_0& props = health_info.legacy.legacy;
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
@@ -364,17 +359,17 @@ void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo
props.batteryHealth, props.batteryStatus);
len = strlen(dmesgline);
- if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
props.batteryCurrent);
}
- if (!healthd_config.batteryFullChargePath.isEmpty()) {
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
props.batteryFullCharge);
}
- if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
props.batteryCycleCount);
}
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp
new file mode 100644
index 000000000..5fd2597d6
--- /dev/null
+++ b/healthd/HealthServiceHealthd.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd"
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/service.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::OK;
+using android::NAME_NOT_FOUND;
+using android::hardware::health::V1_0::HealthConfig;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::Result;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+
+using IHealthLegacy = android::hardware::health::V1_0::IHealth;
+
+static android::sp<IHealthLegacy> gHealth_1_0;
+
+static int healthd_board_get_energy_counter(int64_t* energy) {
+ if (gHealth_1_0 == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
+ Result result = Result::NOT_SUPPORTED;
+ gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) {
+ result = ret;
+ *energy = energyOut;
+ });
+
+ return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+ gHealth_1_0 = IHealthLegacy::getService();
+
+ if (gHealth_1_0 == nullptr) {
+ return;
+ }
+
+ HealthConfig halConfig{};
+ convertToHealthConfig(config, halConfig);
+ gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) {
+ convertFromHealthConfig(halConfigOut, config);
+ // always redirect energy counter queries
+ config->energyCounter = healthd_board_get_energy_counter;
+ });
+ LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL";
+}
+
+// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update
+// with logthis returned.
+int healthd_board_battery_update(struct android::BatteryProperties* props) {
+ int logthis = 0;
+
+ if (gHealth_1_0 == nullptr) {
+ return logthis;
+ }
+
+ HealthInfo info;
+ convertToHealthInfo(props, info);
+ gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) {
+ logthis = ret;
+ convertFromHealthInfo(infoOut, props);
+ });
+
+ return logthis;
+}
+
+int main() {
+ return health_service_main("backup");
+}
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
deleted file mode 100644
index 5893d10c7..000000000
--- a/healthd/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "libhealthd_charger_test"
- }
- ]
-}
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
index 762771e5e..6960c5d38 100644
--- a/healthd/android.hardware.health@2.0-service.rc
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -2,5 +2,5 @@ service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
class hal
user system
group system
- capabilities WAKE_ALARM BLOCK_SUSPEND
+ capabilities WAKE_ALARM
file /dev/kmsg w
diff --git a/healthd/animation.h b/healthd/animation.h
index c2d5f1c04..d02d7a7ea 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -18,7 +18,6 @@
#define HEALTHD_ANIMATION_H
#include <inttypes.h>
-
#include <string>
class GRSurface;
@@ -53,11 +52,20 @@ struct animation {
// - When treating paths as relative paths, it adds ".png" suffix.
// - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
// is added here.
- // If |backup_root| is provided, additionally check if file under |root| is accessbile or not.
- // If not accessbile, use |backup_root| instead.
- // Require that |root| starts and ends with "/". If |backup_root| is provided, require that
- // |backup_root| starts and ends with "/".
- void set_resource_root(const std::string& root, const std::string& backup_root = "");
+ void set_resource_root(const std::string& root) {
+ if (!animation_file.empty()) {
+ animation_file = root + animation_file + ".png";
+ }
+ if (!fail_file.empty()) {
+ fail_file = root + fail_file + ".png";
+ }
+ if (!text_clock.font_file.empty()) {
+ text_clock.font_file = root + text_clock.font_file + ".png";
+ }
+ if (!text_percent.font_file.empty()) {
+ text_percent.font_file = root + text_percent.font_file + ".png";
+ }
+ }
std::string animation_file;
std::string fail_file;
diff --git a/healthd/api/charger_sysprop-current.txt b/healthd/api/charger_sysprop-current.txt
index e69de29bb..678c847cd 100644
--- a/healthd/api/charger_sysprop-current.txt
+++ b/healthd/api/charger_sysprop-current.txt
@@ -0,0 +1,29 @@
+props {
+ module: "android.sysprop.ChargerProperties"
+ prop {
+ api_name: "disable_init_blank"
+ scope: Internal
+ prop_name: "ro.charger.disable_init_blank"
+ }
+ prop {
+ api_name: "draw_split_offset"
+ type: Long
+ scope: Internal
+ prop_name: "ro.charger.draw_split_offset"
+ }
+ prop {
+ api_name: "draw_split_screen"
+ scope: Internal
+ prop_name: "ro.charger.draw_split_screen"
+ }
+ prop {
+ api_name: "enable_suspend"
+ scope: Internal
+ prop_name: "ro.charger.enable_suspend"
+ }
+ prop {
+ api_name: "no_ui"
+ scope: Internal
+ prop_name: "ro.charger.no_ui"
+ }
+}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index e95efc04c..386ba1a9b 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,9 +33,7 @@
#include <optional>
#include <android-base/file.h>
-#include <android-base/logging.h>
#include <android-base/macros.h>
-#include <android-base/strings.h>
#include <linux/netlink.h>
#include <sys/socket.h>
@@ -60,7 +58,6 @@
#include <health2impl/Health.h>
#include <healthd/healthd.h>
-using std::string_literals::operator""s;
using namespace android;
using android::hardware::Return;
using android::hardware::health::GetHealthServiceOrDefault;
@@ -106,13 +103,7 @@ char* locale;
namespace android {
-// Legacy animation resources are loaded from this directory.
-static constexpr const char* legacy_animation_root = "/res/images/";
-
-// Built-in animation resources are loaded from this directory.
-static constexpr const char* system_animation_root = "/system/etc/res/images/";
-
-// Resources in /product/etc/res overrides resources in /res and /system/etc/res.
+// Resources in /product/etc/res overrides resources in /res.
// If the device is using the Generic System Image (GSI), resources may exist in
// both paths.
static constexpr const char* product_animation_desc_path =
@@ -634,12 +625,6 @@ void Charger::InitAnimation() {
batt_anim_.set_resource_root(product_animation_root);
} else if (base::ReadFileToString(animation_desc_path, &content)) {
parse_success = parse_animation_desc(content, &batt_anim_);
- // Fallback resources always exist in system_animation_root. On legacy devices with an old
- // ramdisk image, resources may be overridden under root. For example,
- // /res/images/charger/battery_fail.png may not be the same as
- // system/core/healthd/images/battery_fail.png in the source tree, but is a device-specific
- // image. Hence, load from /res, and fall back to /system/etc/res.
- batt_anim_.set_resource_root(legacy_animation_root, system_animation_root);
} else {
LOGW("Could not open animation description at %s\n", animation_desc_path);
parse_success = false;
@@ -648,13 +633,13 @@ void Charger::InitAnimation() {
if (!parse_success) {
LOGW("Could not parse animation description. Using default animation.\n");
batt_anim_ = BASE_ANIMATION;
- batt_anim_.animation_file.assign(system_animation_root + "charger/battery_scale.png"s);
+ batt_anim_.animation_file.assign("charger/battery_scale");
InitDefaultAnimationFrames();
batt_anim_.frames = owned_frames_.data();
batt_anim_.num_frames = owned_frames_.size();
}
if (batt_anim_.fail_file.empty()) {
- batt_anim_.fail_file.assign(system_animation_root + "charger/battery_fail.png"s);
+ batt_anim_.fail_file.assign("charger/battery_fail");
}
LOGV("Animation Description:\n");
@@ -693,11 +678,10 @@ void Charger::Init(struct healthd_config* config) {
InitAnimation();
- ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
+ ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
- ret = CreateDisplaySurface((system_animation_root + "charger/battery_fail.png"s).c_str(),
- &surf_unknown_);
+ ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load built in battery_fail image\n");
surf_unknown_ = NULL;
@@ -708,8 +692,8 @@ void Charger::Init(struct healthd_config* config) {
int scale_count;
int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
// chunk). We are using hard-coded frame.disp_time instead.
- ret = CreateMultiDisplaySurface(batt_anim_.animation_file, &scale_count, &scale_fps,
- &scale_frames);
+ ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
+ &scale_fps, &scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
batt_anim_.num_frames = 0;
@@ -738,43 +722,6 @@ void Charger::Init(struct healthd_config* config) {
boot_min_cap_ = config->boot_min_cap;
}
-int Charger::CreateDisplaySurface(const std::string& name, GRSurface** surface) {
- return res_create_display_surface(name.c_str(), surface);
-}
-
-int Charger::CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
- GRSurface*** surface) {
- return res_create_multi_display_surface(name.c_str(), frames, fps, surface);
-}
-
-void set_resource_root_for(const std::string& root, const std::string& backup_root,
- std::string* value) {
- if (value->empty()) {
- return;
- }
-
- std::string new_value = root + *value + ".png";
- // If |backup_root| is provided, additionally check whether the file under |root| is
- // accessible or not. If not accessible, fallback to file under |backup_root|.
- if (!backup_root.empty() && access(new_value.data(), F_OK) == -1) {
- new_value = backup_root + *value + ".png";
- }
-
- *value = new_value;
-}
-
-void animation::set_resource_root(const std::string& root, const std::string& backup_root) {
- CHECK(android::base::StartsWith(root, "/") && android::base::EndsWith(root, "/"))
- << "animation root " << root << " must start and end with /";
- CHECK(backup_root.empty() || (android::base::StartsWith(backup_root, "/") &&
- android::base::EndsWith(backup_root, "/")))
- << "animation backup root " << backup_root << " must start and end with /";
- set_resource_root_for(root, backup_root, &animation_file);
- set_resource_root_for(root, backup_root, &fail_file);
- set_resource_root_for(root, backup_root, &text_clock.font_file);
- set_resource_root_for(root, backup_root, &text_percent.font_file);
-}
-
} // namespace android
int healthd_charger_main(int argc, char** argv) {
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 6f9ae8c0e..6e569ee54 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -53,11 +53,6 @@ class Charger : public ::android::hardware::health::V2_1::implementation::HalHea
// HalHealthLoop overrides
void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
- // Allowed to be mocked for testing.
- virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);
- virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
- GRSurface*** surface);
-
private:
void InitDefaultAnimationFrames();
void UpdateScreenState(int64_t now);
diff --git a/healthd/healthd_mode_charger_test.cpp b/healthd/healthd_mode_charger_test.cpp
deleted file mode 100644
index f444f66ad..000000000
--- a/healthd/healthd_mode_charger_test.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sysexits.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <health/utils.h>
-
-#include "healthd_mode_charger.h"
-
-using android::hardware::Return;
-using android::hardware::health::InitHealthdConfig;
-using std::string_literals::operator""s;
-using testing::_;
-using testing::Invoke;
-using testing::NiceMock;
-using testing::StrEq;
-using testing::Test;
-
-namespace android {
-
-// A replacement to ASSERT_* to be used in a forked process. When the condition is not met,
-// print a gtest message, then exit abnormally.
-class ChildAssertHelper : public std::stringstream {
- public:
- ChildAssertHelper(bool res, const char* expr, const char* file, int line) : res_(res) {
- (*this) << file << ":" << line << ": `" << expr << "` evaluates to false\n";
- }
- ~ChildAssertHelper() {
- EXPECT_TRUE(res_) << str();
- if (!res_) exit(EX_SOFTWARE);
- }
-
- private:
- bool res_;
- DISALLOW_COPY_AND_ASSIGN(ChildAssertHelper);
-};
-#define CHILD_ASSERT_TRUE(expr) ChildAssertHelper(expr, #expr, __FILE__, __LINE__)
-
-// Run |test_body| in a chroot jail in a forked process. |subdir| is a sub-directory in testdata.
-// Within |test_body|,
-// - non-fatal errors may be reported using EXPECT_* macro as usual.
-// - fatal errors must be reported using CHILD_ASSERT_TRUE macro. ASSERT_* must not be used.
-void ForkTest(const std::string& subdir, const std::function<void(void)>& test_body) {
- pid_t pid = fork();
- ASSERT_GE(pid, 0) << "Fork fails: " << strerror(errno);
- if (pid == 0) {
- // child
- CHILD_ASSERT_TRUE(
- chroot((android::base::GetExecutableDirectory() + "/" + subdir).c_str()) != -1)
- << "Failed to chroot to " << subdir << ": " << strerror(errno);
- test_body();
- // EXPECT_* macros may set the HasFailure bit without calling exit(). Set exit status
- // accordingly.
- exit(::testing::Test::HasFailure() ? EX_SOFTWARE : EX_OK);
- }
- // parent
- int status;
- ASSERT_NE(-1, waitpid(pid, &status, 0)) << "waitpid() fails: " << strerror(errno);
- ASSERT_TRUE(WIFEXITED(status)) << "Test fails, waitpid() returns " << status;
- ASSERT_EQ(EX_OK, WEXITSTATUS(status)) << "Test fails, child process returns " << status;
-}
-
-class MockHealth : public android::hardware::health::V2_1::IHealth {
- MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, registerCallback,
- (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
- MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, unregisterCallback,
- (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
- MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, update, ());
- MOCK_METHOD(Return<void>, getChargeCounter, (getChargeCounter_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getCurrentNow, (getCurrentNow_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getCurrentAverage, (getCurrentAverage_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getCapacity, (getCapacity_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getEnergyCounter, (getEnergyCounter_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getChargeStatus, (getChargeStatus_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getStorageInfo, (getStorageInfo_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getDiskStats, (getDiskStats_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getHealthInfo, (getHealthInfo_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getHealthConfig, (getHealthConfig_cb _hidl_cb));
- MOCK_METHOD(Return<void>, getHealthInfo_2_1, (getHealthInfo_2_1_cb _hidl_cb));
- MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));
-};
-
-class TestCharger : public Charger {
- public:
- // Inherit constructor.
- using Charger::Charger;
- // Expose protected functions to be used in tests.
- void Init(struct healthd_config* config) override { Charger::Init(config); }
- MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));
- MOCK_METHOD(int, CreateMultiDisplaySurface,
- (const std::string& name, int* frames, int* fps, GRSurface*** surface));
-};
-
-// Intentionally leak TestCharger instance to avoid calling ~HealthLoop() because ~HealthLoop()
-// should never be called. But still verify expected calls upon destruction.
-class VerifiedTestCharger {
- public:
- VerifiedTestCharger(TestCharger* charger) : charger_(charger) {
- testing::Mock::AllowLeak(charger_);
- }
- TestCharger& operator*() { return *charger_; }
- TestCharger* operator->() { return charger_; }
- ~VerifiedTestCharger() { testing::Mock::VerifyAndClearExpectations(charger_); }
-
- private:
- TestCharger* charger_;
-};
-
-// Do not use SetUp and TearDown of a test suite, as they will be invoked in the parent process, not
-// the child process. In particular, if the test suite contains mocks, they will not be verified in
-// the child process. Instead, create mocks within closures in each tests.
-void ExpectChargerResAt(const std::string& root) {
- sp<NiceMock<MockHealth>> health(new NiceMock<MockHealth>());
- VerifiedTestCharger charger(new NiceMock<TestCharger>(health));
-
- // Only one frame in all testdata/**/animation.txt
- GRSurface* multi[] = {nullptr};
-
- EXPECT_CALL(*charger, CreateDisplaySurface(StrEq(root + "charger/battery_fail.png"), _))
- .WillRepeatedly(Invoke([](const auto&, GRSurface** surface) {
- *surface = nullptr;
- return 0;
- }));
- EXPECT_CALL(*charger,
- CreateMultiDisplaySurface(StrEq(root + "charger/battery_scale.png"), _, _, _))
- .WillRepeatedly(Invoke([&](const auto&, int* frames, int* fps, GRSurface*** surface) {
- *frames = arraysize(multi);
- *fps = 60; // Unused fps value
- *surface = multi;
- return 0;
- }));
- struct healthd_config healthd_config;
- InitHealthdConfig(&healthd_config);
- charger->Init(&healthd_config);
-};
-
-// Test that if resources does not exist in /res or in /product/etc/res, load from /system.
-TEST(ChargerLoadAnimationRes, Empty) {
- ForkTest("empty", std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
-}
-
-// Test loading everything from /res
-TEST(ChargerLoadAnimationRes, Legacy) {
- ForkTest("legacy", std::bind(&ExpectChargerResAt, "/res/images/"));
-}
-
-// Test loading animation text from /res but images from /system if images does not exist under
-// /res.
-TEST(ChargerLoadAnimationRes, LegacyTextSystemImages) {
- ForkTest("legacy_text_system_images",
- std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
-}
-
-// Test loading everything from /product
-TEST(ChargerLoadAnimationRes, Product) {
- ForkTest("product", std::bind(&ExpectChargerResAt, "/product/etc/res/images/"));
-}
-
-} // namespace android
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 3cda727be..fadb5a58c 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -66,9 +66,6 @@ class BatteryMonitor {
void logValues(void);
bool isChargerOnline();
- static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
- const struct healthd_config& healthd_config);
-
private:
struct healthd_config *mHealthdConfig;
Vector<String8> mChargerNames;
diff --git a/healthd/manifest_healthd.xml b/healthd/manifest_healthd.xml
new file mode 100644
index 000000000..097a7d87a
--- /dev/null
+++ b/healthd/manifest_healthd.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.hardware.health</name>
+ <transport>hwbinder</transport>
+ <version>2.0</version>
+ <interface>
+ <name>IHealth</name>
+ <instance>backup</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/healthd/testdata/Android.bp b/healthd/testdata/Android.bp
deleted file mode 100644
index f212538d6..000000000
--- a/healthd/testdata/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "libhealthd_charger_test_data",
- srcs: ["**/*.*"],
-}
diff --git a/healthd/testdata/empty/ensure_directory_creation.txt b/healthd/testdata/empty/ensure_directory_creation.txt
deleted file mode 100644
index 36ceff465..000000000
--- a/healthd/testdata/empty/ensure_directory_creation.txt
+++ /dev/null
@@ -1 +0,0 @@
-File is placed to ensure directory is created on the device.
diff --git a/healthd/testdata/legacy/res/images/charger/battery_scale.png b/healthd/testdata/legacy/res/images/charger/battery_scale.png
deleted file mode 100644
index e69de29bb..000000000
--- a/healthd/testdata/legacy/res/images/charger/battery_scale.png
+++ /dev/null
diff --git a/healthd/testdata/legacy/res/values/charger/animation.txt b/healthd/testdata/legacy/res/values/charger/animation.txt
deleted file mode 100644
index 075333676..000000000
--- a/healthd/testdata/legacy/res/values/charger/animation.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Sample Animation file for testing.
-
-# animation: num_cycles, first_frame_repeats, animation_file
-animation: 2 1 charger/battery_scale
-
-fail: charger/battery_fail
-
-# frame: disp_time min_level max_level
-frame: 15 0 100
diff --git a/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
deleted file mode 100644
index 075333676..000000000
--- a/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Sample Animation file for testing.
-
-# animation: num_cycles, first_frame_repeats, animation_file
-animation: 2 1 charger/battery_scale
-
-fail: charger/battery_fail
-
-# frame: disp_time min_level max_level
-frame: 15 0 100
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
deleted file mode 100644
index e69de29bb..000000000
--- a/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
+++ /dev/null
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
deleted file mode 100644
index e69de29bb..000000000
--- a/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
+++ /dev/null
diff --git a/healthd/testdata/product/product/etc/res/values/charger/animation.txt b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
deleted file mode 100644
index 075333676..000000000
--- a/healthd/testdata/product/product/etc/res/values/charger/animation.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-# Sample Animation file for testing.
-
-# animation: num_cycles, first_frame_repeats, animation_file
-animation: 2 1 charger/battery_scale
-
-fail: charger/battery_fail
-
-# frame: disp_time min_level max_level
-frame: 15 0 100
diff --git a/healthd/tests/Android.mk b/healthd/tests/Android.mk
new file mode 100644
index 000000000..87e8862d0
--- /dev/null
+++ b/healthd/tests/Android.mk
@@ -0,0 +1,21 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AnimationParser_test.cpp \
+
+LOCAL_MODULE := healthd_test
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_LIBRARIES := \
+ libhealthd_internal \
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libbase \
+ libcutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/healthd/AnimationParser_test.cpp b/healthd/tests/AnimationParser_test.cpp
index 2fc3185e1..2fc3185e1 100644
--- a/healthd/AnimationParser_test.cpp
+++ b/healthd/tests/AnimationParser_test.cpp
diff --git a/include/android/log.h b/include/android/log.h
new file mode 120000
index 000000000..736c44840
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1 @@
+../../liblog/include/android/log.h \ No newline at end of file
diff --git a/include/backtrace b/include/backtrace
new file mode 120000
index 000000000..93ce2b1e9
--- /dev/null
+++ b/include/backtrace
@@ -0,0 +1 @@
+../libbacktrace/include/backtrace \ No newline at end of file
diff --git a/include/log b/include/log
new file mode 120000
index 000000000..714065f45
--- /dev/null
+++ b/include/log
@@ -0,0 +1 @@
+../liblog/include/log \ No newline at end of file
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
new file mode 120000
index 000000000..f310b35f5
--- /dev/null
+++ b/include/private/android_filesystem_capability.h
@@ -0,0 +1 @@
+../../libcutils/include/private/android_filesystem_capability.h \ No newline at end of file
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
new file mode 120000
index 000000000..f28a5641f
--- /dev/null
+++ b/include/private/android_filesystem_config.h
@@ -0,0 +1 @@
+../../libcutils/include/private/android_filesystem_config.h \ No newline at end of file
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
new file mode 120000
index 000000000..f187a6d22
--- /dev/null
+++ b/include/private/android_logger.h
@@ -0,0 +1 @@
+../../liblog/include/private/android_logger.h \ No newline at end of file
diff --git a/include/private/canned_fs_config.h b/include/private/canned_fs_config.h
new file mode 120000
index 000000000..8f92b2d6a
--- /dev/null
+++ b/include/private/canned_fs_config.h
@@ -0,0 +1 @@
+../../libcutils/include/private/canned_fs_config.h \ No newline at end of file
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
new file mode 100644
index 000000000..e9868a41f
--- /dev/null
+++ b/include/private/fs_config.h
@@ -0,0 +1,4 @@
+// TODO(b/63135587) remove this file after the transitive dependency
+// from private/android_filesystem_config.h is resolved. All files that use
+// libcutils/include/private/fs_config.h should include the file directly, not
+// indirectly via private/android_filesystem_config.h.
diff --git a/include/sysutils b/include/sysutils
new file mode 120000
index 000000000..1c8e85bd1
--- /dev/null
+++ b/include/sysutils
@@ -0,0 +1 @@
+../libsysutils/include/sysutils/ \ No newline at end of file
diff --git a/init/Android.bp b/init/Android.bp
index 7eeafa24b..4865694f3 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -14,23 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["system_core_init_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_core_init_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
init_common_sources = [
"action.cpp",
"action_manager.cpp",
@@ -77,7 +60,6 @@ init_device_sources = [
"selabel.cpp",
"selinux.cpp",
"sigchld_handler.cpp",
- "snapuserd_transition.cpp",
"switch_root.cpp",
"uevent_listener.cpp",
"ueventd.cpp",
@@ -91,6 +73,7 @@ init_host_sources = [
cc_defaults {
name: "init_defaults",
+ cpp_std: "experimental",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
},
@@ -143,15 +126,11 @@ cc_defaults {
"libcgrouprc_format",
"liblmkd_utils",
"libmodprobe",
- "libprocinfo",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
- "libsnapshot_cow",
"libsnapshot_init",
- "libxml2",
"lib_apex_manifest_proto_lite",
- "update_metadata-protos",
],
shared_libs: [
"libbacktrace",
@@ -184,9 +163,6 @@ cc_library_static {
"selinux_policy_version",
],
srcs: init_common_sources + init_device_sources,
- generated_sources: [
- "apex-info-list",
- ],
whole_static_libs: [
"libcap",
"com.android.sysprop.apex",
@@ -201,12 +177,6 @@ cc_library_static {
target: {
recovery: {
cflags: ["-DRECOVERY"],
- exclude_static_libs: [
- "libxml2",
- ],
- exclude_generated_sources: [
- "apex-info-list",
- ],
exclude_shared_libs: [
"libbinder",
"libutils",
@@ -241,130 +211,12 @@ cc_binary {
target: {
recovery: {
cflags: ["-DRECOVERY"],
- exclude_static_libs: [
- "libxml2",
- ],
exclude_shared_libs: [
"libbinder",
"libutils",
],
},
},
- visibility: ["//packages/modules/Virtualization/microdroid"],
-}
-
-// This currently is only for the VM usecase.
-// TODO(jiyong): replace init_first_stage in Android.mk with this
-cc_binary {
- name: "init_first_stage_soong",
- stem: "init_vendor",
-
- srcs: [
- "block_dev_initializer.cpp",
- "devices.cpp",
- "first_stage_console.cpp",
- "first_stage_init.cpp",
- "first_stage_main.cpp",
- "first_stage_mount.cpp",
- "reboot_utils.cpp",
- "selabel.cpp",
- "selinux.cpp",
- "service_utils.cpp",
- "snapuserd_transition.cpp",
- "switch_root.cpp",
- "uevent_listener.cpp",
- "util.cpp",
- ],
-
- static_libs: [
- "libc++fs",
- "libfs_avb",
- "libfs_mgr",
- "libfec",
- "libfec_rs",
- "libsquashfs_utils",
- "liblogwrap",
- "libext4_utils",
- "libcrypto_utils",
- "libsparse",
- "libavb",
- "libkeyutils",
- "liblp",
- "libcutils",
- "libbase",
- "liblog",
- "libcrypto_static",
- "libdl",
- "libz",
- "libselinux",
- "libcap",
- "libgsi",
- "libcom.android.sysprop.apex",
- "liblzma",
- "libunwindstack_no_dex",
- "libbacktrace_no_dex",
- "libmodprobe",
- "libext2_uuid",
- "libprotobuf-cpp-lite",
- "libsnapshot_cow",
- "libsnapshot_init",
- "update_metadata-protos",
- "libprocinfo",
- ],
-
- static_executable: true,
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Wno-unused-parameter",
- "-Werror",
- "-DALLOW_FIRST_STAGE_CONSOLE=0",
- "-DALLOW_LOCAL_PROP_OVERRIDE=0",
- "-DALLOW_PERMISSIVE_SELINUX=0",
- "-DREBOOT_BOOTLOADER_ON_PANIC=0",
- "-DWORLD_WRITABLE_KMSG=0",
- "-DDUMP_ON_UMOUNT_FAILURE=0",
- "-DSHUTDOWN_ZERO_TIMEOUT=0",
- "-DLOG_UEVENTS=0",
- "-DSEPOLICY_VERSION=30", // TODO(jiyong): externalize the version number
- ],
-
- product_variables: {
- debuggable: {
- cflags: [
- "-UALLOW_FIRST_STAGE_CONSOLE",
- "-DALLOW_FIRST_STAGE_CONSOLE=1",
-
- "-UALLOW_LOCAL_PROP_OVERRIDE",
- "-DALLOW_LOCAL_PROP_OVERRIDE=1",
-
- "-UALLOW_PERMISSIVE_SELINUX",
- "-DALLOW_PERMISSIVE_SELINUX=1",
-
- "-UREBOOT_BOOTLOADER_ON_PANIC",
- "-DREBOOT_BOOTLOADER_ON_PANIC=1",
-
- "-UWORLD_WRITABLE_KMSG",
- "-DWORLD_WRITABLE_KMSG=1",
-
- "-UDUMP_ON_UMOUNT_FAILURE",
- "-DDUMP_ON_UMOUNT_FAILURE=1",
- ],
- },
-
- eng: {
- cflags: [
- "-USHUTDOWN_ZERO_TIMEOUT",
- "-DSHUTDOWN_ZERO_TIMEOUT=1",
- ],
- },
- },
-
- sanitize: {
- misc_undefined: ["signed-integer-overflow"],
- hwaddress: false,
- },
}
// Tests
@@ -395,7 +247,6 @@ cc_test {
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
- "reboot_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
"subcontext_test.cpp",
@@ -409,6 +260,7 @@ cc_test {
test_suites: [
"cts",
"device-tests",
+ "vts10",
],
}
@@ -469,6 +321,7 @@ genrule {
cc_binary {
name: "host_init_verifier",
host_supported: true,
+ cpp_std: "experimental",
cflags: [
"-Wall",
"-Wextra",
diff --git a/init/Android.mk b/init/Android.mk
index 3c7d95acf..416b732d8 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -57,16 +57,11 @@ LOCAL_SRC_FILES := \
reboot_utils.cpp \
selabel.cpp \
selinux.cpp \
- service_utils.cpp \
- snapuserd_transition.cpp \
switch_root.cpp \
uevent_listener.cpp \
util.cpp \
LOCAL_MODULE := init_first_stage
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
LOCAL_MODULE_STEM := init
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -80,22 +75,12 @@ LOCAL_REQUIRED_MODULES := \
adb_debug.prop \
# Set up the directories that first stage init mounts on.
-
-my_ramdisk_dirs := \
- debug_ramdisk \
- dev \
- metadata \
- mnt \
- proc \
- second_stage_resources \
- sys \
-
-LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(my_ramdisk_dirs))
-ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
- LOCAL_POST_INSTALL_CMD += $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(my_ramdisk_dirs))
-endif
-
-my_ramdisk_dirs :=
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+ $(TARGET_RAMDISK_OUT)/debug_ramdisk \
+ $(TARGET_RAMDISK_OUT)/dev \
+ $(TARGET_RAMDISK_OUT)/mnt \
+ $(TARGET_RAMDISK_OUT)/proc \
+ $(TARGET_RAMDISK_OUT)/sys \
LOCAL_STATIC_LIBRARIES := \
libc++fs \
@@ -127,10 +112,7 @@ LOCAL_STATIC_LIBRARIES := \
libmodprobe \
libext2_uuid \
libprotobuf-cpp-lite \
- libsnapshot_cow \
libsnapshot_init \
- update_metadata-protos \
- libprocinfo \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
@@ -141,9 +123,6 @@ endif
include $(CLEAR_VARS)
LOCAL_MODULE := init_system
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
LOCAL_REQUIRED_MODULES := \
init_second_stage \
@@ -152,9 +131,6 @@ include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
LOCAL_MODULE := init_vendor
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
LOCAL_REQUIRED_MODULES := \
init_first_stage \
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 6f22ab792..95f97e3fd 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -24,9 +24,6 @@
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- <option name="throw-on-error" value="false" />
- </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsInitTestCases" />
diff --git a/init/OWNERS b/init/OWNERS
index 9e70e7dcf..babbe4ded 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1 +1 @@
-dvander@google.com
+tomcherry@google.com
diff --git a/init/README.md b/init/README.md
index 4a262c920..6f2c01f05 100644
--- a/init/README.md
+++ b/init/README.md
@@ -31,13 +31,14 @@ The init language is used in plain text files that take the .rc file
extension. There are typically multiple of these in multiple
locations on the system, described below.
-`/system/etc/init/hw/init.rc` is the primary .rc file and is loaded by the init executable at the
-beginning of its execution. It is responsible for the initial set up of the system.
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution. It is responsible for the initial
+set up of the system.
Init loads all of the files contained within the
-`/{system,system_ext,vendor,odm,product}/etc/init/` directories immediately after loading
-the primary `/system/etc/init/hw/init.rc`. This is explained in more details in the
-[Imports](#imports) section of this file.
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc. This is explained in more details in the
+Imports section of this file.
Legacy devices without the first stage mount mechanism previously were
able to import init scripts during mount_all, however that is deprecated
@@ -172,14 +173,9 @@ runs the service.
This option connects stdin, stdout, and stderr to the console. It is mutually exclusive with the
stdio_to_kmsg option, which only connects stdout and stderr to kmsg.
-`critical [window=<fatal crash window mins>] [target=<fatal reboot target>]`
+`critical`
> This is a device-critical service. If it exits more than four times in
- _fatal crash window mins_ minutes or before boot completes, the device
- will reboot into _fatal reboot target_.
- The default value of _fatal crash window mins_ is 4, and default value
- of _fatal reboot target_ is 'bootloader'.
- For tests, the fatal reboot can be skipped by setting property
- `init.svc_debug.no_fatal.<service-name>` to `true` for specified critical service.
+ four minutes or before boot completes, the device will reboot into bootloader.
`disabled`
> This service will not automatically start with its class.
@@ -201,14 +197,11 @@ runs the service.
Currently defaults to root. (??? probably should default to nobody)
`interface <interface name> <instance name>`
-> Associates this service with a list of the AIDL or HIDL services that it provides. The interface
- name must be a fully-qualified name and not a value name. For instance, this is used to allow
- servicemanager or hwservicemanager to lazily start services. When multiple interfaces are served,
- this tag should be used multiple times. An example of an entry for a HIDL
- interface is `interface vendor.foo.bar@1.0::IBaz default`. For an AIDL interface, use
- `interface aidl <instance name>`. The instance name for an AIDL interface is
- whatever is registered with servicemanager, and these can be listed with `adb
- shell dumpsys -l`.
+> Associates this service with a list of the HIDL services that it provides. The interface name
+ must be a fully-qualified name and not a value name. For instance, this is used to allow
+ hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
+ be used multiple times.
+ For example: interface vendor.foo.bar@1.0::IBaz default
`ioprio <class> <priority>`
> Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.
@@ -453,10 +446,6 @@ Commands
exist. And it will be truncated if dst file is a normal regular file and
already exists.
-`copy_per_line <src> <dst>`
-> Copies a file line by line. Similar to copy, but useful for dst is a sysfs node
- that doesn't handle multiple lines of data.
-
`domainname <name>`
> Set the domain name.
@@ -523,11 +512,6 @@ instance. \
`interface_start aidl/aidl_lazy_test_1` will start the AIDL service that
provides the `aidl_lazy_test_1` interface.
-`load_exports <path>`
-> Open the file at _path_ and export global environment variables declared
- there. Each line must be in the format `export <name> <value>`, as described
- above.
-
`load_system_props`
> (This action is deprecated and no-op.)
@@ -580,11 +564,9 @@ provides the `aidl_lazy_test_1` interface.
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
a comma separated string, e.g. barrier=1,noauto\_da\_alloc
-`perform_apex_config`
-> Performs tasks after APEXes are mounted. For example, creates data directories
- for the mounted APEXes, parses config file(s) from them, and updates linker
- configurations. Intended to be used only once when apexd notifies the mount
- event by setting `apexd.status` to ready.
+`parse_apex_configs`
+> Parses config file(s) from the mounted APEXes. Intended to be used only once
+ when apexd notifies the mount event by setting apexd.status to ready.
`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
@@ -702,22 +684,29 @@ imports are handled as a file is being parsed and follow the below logic.
There are only three times where the init executable imports .rc files:
- 1. When it imports `/system/etc/init/hw/init.rc` or the script indicated by the property
+ 1. When it imports /init.rc or the script indicated by the property
`ro.boot.init_rc` during initial boot.
- 2. When it imports `/{system,system_ext,vendor,odm,product}/etc/init/` immediately after
- importing `/system/etc/init/hw/init.rc`.
+ 2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
+ devices immediately after importing /init.rc.
3. (Deprecated) When it imports /{system,vendor,odm}/etc/init/ or .rc files
at specified paths during mount_all, not allowed for devices launching
after Q.
-The order that files are imported is a bit complex for legacy reasons. The below is guaranteed:
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility. It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
-1. `/system/etc/init/hw/init.rc` is parsed then recursively each of its imports are
+Nonetheless, the de facto order for first stage mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
parsed.
-2. The contents of `/system/etc/init/` are alphabetized and parsed sequentially, with imports
- happening recursively after each file is parsed.
-3. Step 2 is repeated for `/system_ext/etc/init`, `/vendor/etc/init`, `/odm/etc/init`,
- `/product/etc/init`
+2. The contents of /system/etc/init/ are alphabetized and parsed
+ sequentially, with imports happening recursively after each file is
+ parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
The below pseudocode may explain this more clearly:
@@ -726,17 +715,13 @@ The below pseudocode may explain this more clearly:
for (import : file.imports)
Import(import)
- Import(/system/etc/init/hw/init.rc)
- Directories = [/system/etc/init, /system_ext/etc/init, /vendor/etc/init, /odm/etc/init, /product/etc/init]
+ Import(/init.rc)
+ Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
for (directory : Directories)
files = <Alphabetical order of directory's contents>
for (file : files)
Import(file)
-Actions are executed in the order that they are parsed. For example the `post-fs-data` action(s)
-in `/system/etc/init/hw/init.rc` are always the first `post-fs-data` action(s) to be executed in
-order of how they appear in that file. Then the `post-fs-data` actions of the imports of
-`/system/etc/init/hw/init.rc` in the order that they're imported, etc.
Properties
----------
@@ -777,7 +762,7 @@ The _commands_ are listed below.
These are equivalent to using the `start`, `restart`, and `stop` commands on the service specified
by the _value_ of the property.
-`oneshot_on` and `oneshot_off` will turn on or off the _oneshot_
+`oneshot_one` and `oneshot_off` will turn on or off the _oneshot_
flag for the service specified by the _value_ of the property. This is
particularly intended for services that are conditionally lazy HALs. When
they are lazy HALs, oneshot must be on, otherwise oneshot should be off.
@@ -800,9 +785,6 @@ Init records some boot timing information in system properties.
`ro.boottime.init.selinux`
> How long in ns it took to run SELinux stage.
-`ro.boottime.init.modules`
-> How long in ms it took to load kernel modules.
-
`ro.boottime.init.cold_boot_wait`
> How long init waited for ueventd's coldboot phase to end.
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index d22f68fb0..053ebf813 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -13,16 +13,6 @@ For example
uevent_socket_rcvbuf_size 16M
Sets the uevent socket rcvbuf_size to 16 megabytes.
-## Importing configuration files
---------------------------------
-Ueventd reads /system/etc/ueventd.rc, all other files are imported via the `import` command, which
-takes the format of
-
- import <path>
-This command parses an ueventd config file, extending the current configuration. If _path_ is a
-directory, each file in the directory is parsed as a config file. It is not recursive, nested
-directories will not be parsed. Imported files are parsed after the current file has been parsed.
-
## /dev
----
Ueventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the
@@ -42,7 +32,7 @@ for the node path:
The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
lines take the format of
- devname mode uid gid [options]
+ devname mode uid gid
For example
/dev/null 0666 root root
@@ -80,7 +70,7 @@ Ueventd by default takes no action for `/sys`, however it can be instructed to s
certain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script
and a line that begins with `/sys`. These lines take the format of
- nodename attr mode uid gid [options]
+ nodename attr mode uid gid
For example
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
@@ -88,15 +78,7 @@ When a uevent that matches the pattern `/sys/devices/system/cpu/cpu*` is sent, t
attribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and
its group set to `system`.
-## Path matching
-----------------
-The path for a `/dev` or `/sys` entry can contain a `*` anywhere in the path.
-1. If the only `*` appears at the end of the string or if the _options_ parameter is set to
-`no_fnm_pathname`, ueventd matches the entry by `fnmatch(entry_path, incoming_path, 0)`
-2. Otherwise, ueventd matches the entry by `fnmatch(entry_path, incoming_path, FNM_PATHNAME)`
-
-See the [man page for fnmatch](https://www.man7.org/linux/man-pages/man3/fnmatch.3.html) for more
-details.
+Note that `*` matches as a wildcard and can be used anywhere in a path.
## Firmware loading
----------------
@@ -104,8 +86,6 @@ Ueventd by default serves firmware requests by searching through a list of firmw
for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
kernel.
-`/apex/*/etc/firmware` is also searched after a list of firmware directories.
-
The list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc
file. This line takes the format of
@@ -130,10 +110,6 @@ For example
Will launch `/vendor/bin/led_coeffs.bin` as the system user instead of serving the default firmware
for `/devices/leds/red/firmware/coeffs.bin`.
-The `devpath` argument may include asterisks (`*`) to match multiple paths. For example, the string
-`/dev/*/red` will match `/dev/leds/red` as well as `/dev/lights/red`. The pattern matching follows
-the rules of the fnmatch() function.
-
Ueventd will provide the uevent `DEVPATH` and `FIRMWARE` to this external program on the environment
via environment variables with the same names. Ueventd will use the string written to stdout as the
new name of the firmware to load. It will still look for the new firmware in the list of firmware
@@ -155,8 +131,8 @@ ueventd to create the nodes.
For boot time purposes, this is done in parallel across a set of child processes. `ueventd.cpp` in
this directory contains documentation on how the parallelization is done.
-There is an option to parallelize the restorecon function during cold boot as well. It is
-recommended that devices use genfscon for labeling sysfs nodes. However, some devices may benefit
-from enabling the parallelization option:
+There is an option to parallelize the restorecon function during cold boot as well. This should only
+be done for devices that do not use genfscon, which is the recommended method for labeling sysfs
+nodes. To enable this option, use the below line in a ueventd.rc script:
parallel_restorecon enabled
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 9c2a7bb14..b423f864e 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -37,15 +37,7 @@ BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024)
}
bool BlockDevInitializer::InitDeviceMapper() {
- return InitMiscDevice("device-mapper");
-}
-
-bool BlockDevInitializer::InitDmUser(const std::string& name) {
- return InitMiscDevice("dm-user!" + name);
-}
-
-bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
- const std::string dm_path = "/devices/virtual/misc/" + name;
+ const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
@@ -57,13 +49,13 @@ bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
- LOG(INFO) << name << " device not found in /sys, waiting for its uevent";
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(dm_callback, 10s);
- LOG(INFO) << "Wait for " << name << " returned after " << t;
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
}
if (!found) {
- LOG(ERROR) << name << " device not found after polling timeout";
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
return false;
}
return true;
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index ec39ce084..0d4c6e9fc 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#pragma once
-
#include <memory>
#include <set>
#include <string>
@@ -29,15 +27,12 @@ class BlockDevInitializer final {
BlockDevInitializer();
bool InitDeviceMapper();
- bool InitDmUser(const std::string& name);
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
- bool InitMiscDevice(const std::string& name);
-
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 035038f32..0ac66f272 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -88,7 +88,6 @@ using namespace std::literals::string_literals;
using android::base::Basename;
using android::base::SetProperty;
-using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -292,38 +291,6 @@ static Result<void> do_export(const BuiltinArguments& args) {
return {};
}
-static Result<void> do_load_exports(const BuiltinArguments& args) {
- auto file_contents = ReadFile(args[1]);
- if (!file_contents.ok()) {
- return Error() << "Could not read input file '" << args[1]
- << "': " << file_contents.error();
- }
-
- auto lines = Split(*file_contents, "\n");
- for (const auto& line : lines) {
- if (line.empty()) {
- continue;
- }
-
- auto env = Split(line, " ");
-
- if (env.size() != 3) {
- return ErrnoError() << "Expected a line as `export <name> <value>`, found: `" << line
- << "`";
- }
-
- if (env[0] != "export") {
- return ErrnoError() << "Unknown action: '" << env[0] << "', expected 'export'";
- }
-
- if (setenv(env[1].c_str(), env[2].c_str(), 1) == -1) {
- return ErrnoError() << "Failed to export '" << line << "' from " << args[1];
- }
- }
-
- return {};
-}
-
static Result<void> do_hostname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result.ok()) {
return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
@@ -603,6 +570,7 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
}
SetProperty("ro.crypto.state", "encrypted");
+ SetProperty("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
@@ -627,6 +595,7 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
+ SetProperty("ro.crypto.type", "file");
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
@@ -637,6 +606,7 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
+ SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -647,6 +617,7 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
+ SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -695,26 +666,18 @@ static Result<void> do_mount_all(const BuiltinArguments& args) {
}
}
- auto mount_fstab_result = fs_mgr_mount_all(&fstab, mount_all->mode);
+ auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_all->mode);
SetProperty(prop_name, std::to_string(t.duration().count()));
if (mount_all->import_rc) {
import_late(mount_all->rc_paths);
}
- if (mount_fstab_result.userdata_mounted) {
- // This call to fs_mgr_mount_all mounted userdata. Keep the result in
- // order for userspace reboot to correctly remount userdata.
- LOG(INFO) << "Userdata mounted using "
- << (mount_all->fstab_path.empty() ? "(default fstab)" : mount_all->fstab_path)
- << " result : " << mount_fstab_result.code;
- initial_mount_fstab_return_code = mount_fstab_result.code;
- }
-
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- auto queue_fs_result = queue_fs_event(mount_fstab_result.code, false);
+ initial_mount_fstab_return_code = mount_fstab_return_code;
+ auto queue_fs_result = queue_fs_event(mount_fstab_return_code, false);
if (!queue_fs_result.ok()) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@@ -893,11 +856,6 @@ static Result<void> do_verity_update_state(const BuiltinArguments& args) {
// for system as root, so it has property [partition.system.verified].
std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
SetProperty("partition." + partition + ".verified", std::to_string(mode));
-
- std::string hash_alg = fs_mgr_get_hashtree_algorithm(entry);
- if (!hash_alg.empty()) {
- SetProperty("partition." + partition + ".verified.hash_alg", hash_alg);
- }
}
return {};
@@ -1006,23 +964,6 @@ static Result<void> do_copy(const BuiltinArguments& args) {
return {};
}
-static Result<void> do_copy_per_line(const BuiltinArguments& args) {
- std::string file_contents;
- if (!android::base::ReadFileToString(args[1], &file_contents, true)) {
- return Error() << "Could not read input file '" << args[1] << "'";
- }
- auto lines = Split(file_contents, "\n");
- for (const auto& line : lines) {
- auto result = WriteFile(args[2], line);
- if (!result.ok()) {
- LOG(VERBOSE) << "Could not write to output file '" << args[2] << "' with '" << line
- << "' : " << result.error();
- }
- }
-
- return {};
-}
-
static Result<void> do_chown(const BuiltinArguments& args) {
auto uid = DecodeUid(args[1]);
if (!uid.ok()) {
@@ -1236,10 +1177,6 @@ static Result<void> do_remount_userdata(const BuiltinArguments& args) {
}
// TODO(b/135984674): check that fstab contains /data.
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
- std::string proc_mounts_output;
- android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
- android::base::WriteStringToFile(proc_mounts_output,
- "/metadata/userspacereboot/mount_info.txt");
trigger_shutdown("reboot,mount_userdata_failed");
}
if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result.ok()) {
@@ -1269,7 +1206,7 @@ static Result<void> do_mark_post_data(const BuiltinArguments& args) {
}
static Result<void> GenerateLinkerConfiguration() {
- const char* linkerconfig_binary = "/apex/com.android.runtime/bin/linkerconfig";
+ const char* linkerconfig_binary = "/system/bin/linkerconfig";
const char* linkerconfig_target = "/linkerconfig";
const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target};
@@ -1278,34 +1215,12 @@ static Result<void> GenerateLinkerConfiguration() {
return ErrnoError() << "failed to execute linkerconfig";
}
- auto current_mount_ns = GetCurrentMountNamespace();
- if (!current_mount_ns.ok()) {
- return current_mount_ns.error();
- }
- if (*current_mount_ns == NS_DEFAULT) {
- SetDefaultMountNamespaceReady();
- }
-
LOG(INFO) << "linkerconfig generated " << linkerconfig_target
<< " with mounted APEX modules info";
return {};
}
-static Result<void> MountLinkerConfigForDefaultNamespace() {
- // No need to mount linkerconfig for default mount namespace if the path does not exist (which
- // would mean it is already mounted)
- if (access("/linkerconfig/default", 0) != 0) {
- return {};
- }
-
- if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
- return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
- }
-
- return {};
-}
-
static bool IsApexUpdatable() {
static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
return updatable;
@@ -1404,14 +1319,11 @@ static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
}
static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
- if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
- return result.error();
- }
- if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
- return result.error();
+ if (SwitchToDefaultMountNamespace()) {
+ return {};
+ } else {
+ return Error() << "Failed to enter into default mount namespace";
}
- LOG(INFO) << "Switched to default mount namespace";
- return {};
}
// Builtin-function-map start
@@ -1429,7 +1341,6 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
{"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},
{"class_stop", {1, 1, {false, do_class_stop}}},
{"copy", {2, 2, {true, do_copy}}},
- {"copy_per_line", {2, 2, {true, do_copy_per_line}}},
{"domainname", {1, 1, {true, do_domainname}}},
{"enable", {1, 1, {false, do_enable}}},
{"exec", {1, kMax, {false, do_exec}}},
@@ -1444,7 +1355,6 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
{"interface_restart", {1, 1, {false, do_interface_restart}}},
{"interface_start", {1, 1, {false, do_interface_start}}},
{"interface_stop", {1, 1, {false, do_interface_stop}}},
- {"load_exports", {1, 1, {false, do_load_exports}}},
{"load_persist_props", {0, 0, {false, do_load_persist_props}}},
{"load_system_props", {0, 0, {false, do_load_system_props}}},
{"loglevel", {1, 1, {false, do_loglevel}}},
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index ab6ff03ef..a91cd1dd1 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -28,56 +28,47 @@ namespace android {
namespace init {
static const std::map<std::string, int> cap_map = {
- CAP_MAP_ENTRY(CHOWN),
- CAP_MAP_ENTRY(DAC_OVERRIDE),
- CAP_MAP_ENTRY(DAC_READ_SEARCH),
- CAP_MAP_ENTRY(FOWNER),
- CAP_MAP_ENTRY(FSETID),
- CAP_MAP_ENTRY(KILL),
- CAP_MAP_ENTRY(SETGID),
- CAP_MAP_ENTRY(SETUID),
- CAP_MAP_ENTRY(SETPCAP),
- CAP_MAP_ENTRY(LINUX_IMMUTABLE),
- CAP_MAP_ENTRY(NET_BIND_SERVICE),
- CAP_MAP_ENTRY(NET_BROADCAST),
- CAP_MAP_ENTRY(NET_ADMIN),
- CAP_MAP_ENTRY(NET_RAW),
- CAP_MAP_ENTRY(IPC_LOCK),
- CAP_MAP_ENTRY(IPC_OWNER),
- CAP_MAP_ENTRY(SYS_MODULE),
- CAP_MAP_ENTRY(SYS_RAWIO),
- CAP_MAP_ENTRY(SYS_CHROOT),
- CAP_MAP_ENTRY(SYS_PTRACE),
- CAP_MAP_ENTRY(SYS_PACCT),
- CAP_MAP_ENTRY(SYS_ADMIN),
- CAP_MAP_ENTRY(SYS_BOOT),
- CAP_MAP_ENTRY(SYS_NICE),
- CAP_MAP_ENTRY(SYS_RESOURCE),
- CAP_MAP_ENTRY(SYS_TIME),
- CAP_MAP_ENTRY(SYS_TTY_CONFIG),
- CAP_MAP_ENTRY(MKNOD),
- CAP_MAP_ENTRY(LEASE),
- CAP_MAP_ENTRY(AUDIT_WRITE),
- CAP_MAP_ENTRY(AUDIT_CONTROL),
- CAP_MAP_ENTRY(SETFCAP),
- CAP_MAP_ENTRY(MAC_OVERRIDE),
- CAP_MAP_ENTRY(MAC_ADMIN),
- CAP_MAP_ENTRY(SYSLOG),
- CAP_MAP_ENTRY(WAKE_ALARM),
- CAP_MAP_ENTRY(BLOCK_SUSPEND),
- CAP_MAP_ENTRY(AUDIT_READ),
-#if defined(__BIONIC__)
- CAP_MAP_ENTRY(PERFMON),
- CAP_MAP_ENTRY(BPF),
- CAP_MAP_ENTRY(CHECKPOINT_RESTORE),
-#endif
+ CAP_MAP_ENTRY(CHOWN),
+ CAP_MAP_ENTRY(DAC_OVERRIDE),
+ CAP_MAP_ENTRY(DAC_READ_SEARCH),
+ CAP_MAP_ENTRY(FOWNER),
+ CAP_MAP_ENTRY(FSETID),
+ CAP_MAP_ENTRY(KILL),
+ CAP_MAP_ENTRY(SETGID),
+ CAP_MAP_ENTRY(SETUID),
+ CAP_MAP_ENTRY(SETPCAP),
+ CAP_MAP_ENTRY(LINUX_IMMUTABLE),
+ CAP_MAP_ENTRY(NET_BIND_SERVICE),
+ CAP_MAP_ENTRY(NET_BROADCAST),
+ CAP_MAP_ENTRY(NET_ADMIN),
+ CAP_MAP_ENTRY(NET_RAW),
+ CAP_MAP_ENTRY(IPC_LOCK),
+ CAP_MAP_ENTRY(IPC_OWNER),
+ CAP_MAP_ENTRY(SYS_MODULE),
+ CAP_MAP_ENTRY(SYS_RAWIO),
+ CAP_MAP_ENTRY(SYS_CHROOT),
+ CAP_MAP_ENTRY(SYS_PTRACE),
+ CAP_MAP_ENTRY(SYS_PACCT),
+ CAP_MAP_ENTRY(SYS_ADMIN),
+ CAP_MAP_ENTRY(SYS_BOOT),
+ CAP_MAP_ENTRY(SYS_NICE),
+ CAP_MAP_ENTRY(SYS_RESOURCE),
+ CAP_MAP_ENTRY(SYS_TIME),
+ CAP_MAP_ENTRY(SYS_TTY_CONFIG),
+ CAP_MAP_ENTRY(MKNOD),
+ CAP_MAP_ENTRY(LEASE),
+ CAP_MAP_ENTRY(AUDIT_WRITE),
+ CAP_MAP_ENTRY(AUDIT_CONTROL),
+ CAP_MAP_ENTRY(SETFCAP),
+ CAP_MAP_ENTRY(MAC_OVERRIDE),
+ CAP_MAP_ENTRY(MAC_ADMIN),
+ CAP_MAP_ENTRY(SYSLOG),
+ CAP_MAP_ENTRY(WAKE_ALARM),
+ CAP_MAP_ENTRY(BLOCK_SUSPEND),
+ CAP_MAP_ENTRY(AUDIT_READ),
};
-#if defined(__BIONIC__)
-static_assert(CAP_LAST_CAP == CAP_CHECKPOINT_RESTORE, "CAP_LAST_CAP is not CAP_CHECKPOINT_RESTORE");
-#else
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
-#endif
static bool ComputeCapAmbientSupported() {
#if defined(__ANDROID__)
diff --git a/init/devices.cpp b/init/devices.cpp
index ce6298a9d..9fbec641b 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -22,7 +22,6 @@
#include <unistd.h>
#include <chrono>
-#include <filesystem>
#include <memory>
#include <string>
#include <thread>
@@ -124,15 +123,8 @@ static bool FindDmDevice(const std::string& path, std::string* name, std::string
return true;
}
-Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid,
- bool no_fnm_pathname)
- : name_(name),
- perm_(perm),
- uid_(uid),
- gid_(gid),
- prefix_(false),
- wildcard_(false),
- no_fnm_pathname_(no_fnm_pathname) {
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+ : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
// Set 'prefix_' or 'wildcard_' based on the below cases:
//
// 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
@@ -143,6 +135,7 @@ Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t
//
// 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
// with FNM_PATHNAME to compare 'name' to a given path.
+
auto wildcard_position = name_.find('*');
if (wildcard_position != std::string::npos) {
if (wildcard_position == name_.length() - 1) {
@@ -156,8 +149,7 @@ Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t
bool Permissions::Match(const std::string& path) const {
if (prefix_) return StartsWith(path, name_);
- if (wildcard_)
- return fnmatch(name_.c_str(), path.c_str(), no_fnm_pathname_ ? 0 : FNM_PATHNAME) == 0;
+ if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
return path == name_;
}
@@ -201,8 +193,7 @@ bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_d
while (directory != "/" && directory != ".") {
std::string subsystem_link_path;
if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
- (subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
- subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
+ subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
// We need to remove the mount point that we added above before returning.
directory.erase(0, sysfs_mount_point_.size());
*platform_device_path = directory;
@@ -468,10 +459,9 @@ void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
}
void DeviceHandler::HandleUevent(const Uevent& uevent) {
- if (uevent.action == "add" || uevent.action == "change" ||
- uevent.action == "bind" || uevent.action == "online") {
- FixupSysPermissions(uevent.path, uevent.subsystem);
- }
+ if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+ FixupSysPermissions(uevent.path, uevent.subsystem);
+ }
// if it's not a /dev device, nothing to do
if (uevent.major < 0 || uevent.minor < 0) return;
@@ -505,8 +495,6 @@ void DeviceHandler::HandleUevent(const Uevent& uevent) {
} else if (StartsWith(uevent.subsystem, "usb")) {
// ignore other USB events
return;
- } else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
- devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
} else {
devpath = "/dev/" + Basename(uevent.path);
}
diff --git a/init/devices.h b/init/devices.h
index d70d746e8..05d64da6e 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -38,7 +38,7 @@ class Permissions {
public:
friend void TestPermissions(const Permissions& expected, const Permissions& test);
- Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid, bool no_fnm_pathname);
+ Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
bool Match(const std::string& path) const;
@@ -56,7 +56,6 @@ class Permissions {
gid_t gid_;
bool prefix_;
bool wildcard_;
- bool no_fnm_pathname_;
};
class SysfsPermissions : public Permissions {
@@ -64,8 +63,8 @@ class SysfsPermissions : public Permissions {
friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);
SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
- gid_t gid, bool no_fnm_pathname)
- : Permissions(name, perm, uid, gid, no_fnm_pathname), attribute_(attribute) {}
+ gid_t gid)
+ : Permissions(name, perm, uid, gid), attribute_(attribute) {}
bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
void SetPermissions(const std::string& path) const;
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index e7bac68bc..c408bc130 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -221,7 +221,7 @@ TEST(device_handler, sanitize_onebad) {
TEST(device_handler, DevPermissionsMatchNormal) {
// Basic from ueventd.rc
// /dev/null 0666 root root
- Permissions permissions("/dev/null", 0666, 0, 0, false);
+ Permissions permissions("/dev/null", 0666, 0, 0);
EXPECT_TRUE(permissions.Match("/dev/null"));
EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
EXPECT_FALSE(permissions.Match("/dev/nul"));
@@ -233,7 +233,7 @@ TEST(device_handler, DevPermissionsMatchNormal) {
TEST(device_handler, DevPermissionsMatchPrefix) {
// Prefix from ueventd.rc
// /dev/dri/* 0666 root graphics
- Permissions permissions("/dev/dri/*", 0666, 0, 1000, false);
+ Permissions permissions("/dev/dri/*", 0666, 0, 1000);
EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
EXPECT_TRUE(permissions.Match("/dev/dri/"));
@@ -246,7 +246,7 @@ TEST(device_handler, DevPermissionsMatchPrefix) {
TEST(device_handler, DevPermissionsMatchWildcard) {
// Wildcard example
// /dev/device*name 0666 root graphics
- Permissions permissions("/dev/device*name", 0666, 0, 1000, false);
+ Permissions permissions("/dev/device*name", 0666, 0, 1000);
EXPECT_TRUE(permissions.Match("/dev/devicename"));
EXPECT_TRUE(permissions.Match("/dev/device123name"));
EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
@@ -260,31 +260,13 @@ TEST(device_handler, DevPermissionsMatchWildcard) {
TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
// Wildcard+Prefix example
// /dev/device*name* 0666 root graphics
- Permissions permissions("/dev/device*name*", 0666, 0, 1000, false);
+ Permissions permissions("/dev/device*name*", 0666, 0, 1000);
EXPECT_TRUE(permissions.Match("/dev/devicename"));
EXPECT_TRUE(permissions.Match("/dev/device123name"));
EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
// FNM_PATHNAME doesn't match '/' with *
EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
- EXPECT_FALSE(permissions.Match("/dev/device/1/2/3name/something"));
- EXPECT_FALSE(permissions.Match("/dev/deviceame"));
- EXPECT_EQ(0666U, permissions.perm());
- EXPECT_EQ(0U, permissions.uid());
- EXPECT_EQ(1000U, permissions.gid());
-}
-
-TEST(device_handler, DevPermissionsMatchWildcardPrefix_NoFnmPathName) {
- // Wildcard+Prefix example with no_fnm_pathname
- // /dev/device*name* 0666 root graphics
- Permissions permissions("/dev/device*name*", 0666, 0, 1000, true);
- EXPECT_TRUE(permissions.Match("/dev/devicename"));
- EXPECT_TRUE(permissions.Match("/dev/device123name"));
- EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
- EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
- // With NoFnmPathName, the below matches, unlike DevPermissionsMatchWildcardPrefix.
- EXPECT_TRUE(permissions.Match("/dev/device123name/something"));
- EXPECT_TRUE(permissions.Match("/dev/device/1/2/3name/something"));
EXPECT_FALSE(permissions.Match("/dev/deviceame"));
EXPECT_EQ(0666U, permissions.perm());
EXPECT_EQ(0U, permissions.uid());
@@ -293,8 +275,7 @@ TEST(device_handler, DevPermissionsMatchWildcardPrefix_NoFnmPathName) {
TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
// /sys/devices/virtual/input/input* enable 0660 root input
- SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001,
- false);
+ SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
EXPECT_EQ(0660U, permissions.perm());
@@ -304,7 +285,7 @@ TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
// /sys/class/input/event* enable 0660 root input
- SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001, false);
+ SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
EXPECT_TRUE(permissions.MatchWithSubsystem(
"/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
EXPECT_FALSE(permissions.MatchWithSubsystem(
@@ -318,7 +299,7 @@ TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
// /sys/bus/i2c/devices/i2c-* enable 0660 root input
- SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001, false);
+ SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
EXPECT_FALSE(
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index bdc292275..dff7b6948 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -17,8 +17,6 @@
#include "firmware_handler.h"
#include <fcntl.h>
-#include <fnmatch.h>
-#include <glob.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
@@ -32,7 +30,6 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -47,20 +44,6 @@ using android::base::WriteFully;
namespace android {
namespace init {
-namespace {
-bool PrefixMatch(const std::string& pattern, const std::string& path) {
- return android::base::StartsWith(path, pattern);
-}
-
-bool FnMatch(const std::string& pattern, const std::string& path) {
- return fnmatch(pattern.c_str(), path.c_str(), 0) == 0;
-}
-
-bool EqualMatch(const std::string& pattern, const std::string& path) {
- return pattern == path;
-}
-} // namespace
-
static void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd,
size_t fw_size, int loading_fd, int data_fd) {
// Start transfer.
@@ -81,22 +64,6 @@ static bool IsBooting() {
return access("/dev/.booting", F_OK) == 0;
}
-ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
- std::string handler_path)
- : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {
- auto wildcard_position = this->devpath.find('*');
- if (wildcard_position != std::string::npos) {
- if (wildcard_position == this->devpath.length() - 1) {
- this->devpath.pop_back();
- match = std::bind(PrefixMatch, this->devpath, std::placeholders::_1);
- } else {
- match = std::bind(FnMatch, this->devpath, std::placeholders::_1);
- }
- } else {
- match = std::bind(EqualMatch, this->devpath, std::placeholders::_1);
- }
-}
-
FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
std::vector<ExternalFirmwareHandler> external_firmware_handlers)
: firmware_directories_(std::move(firmware_directories)),
@@ -191,7 +158,7 @@ Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handl
std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {
for (const auto& external_handler : external_firmware_handlers_) {
- if (external_handler.match(uevent.path)) {
+ if (external_handler.devpath == uevent.path) {
LOG(INFO) << "Launching external firmware handler '" << external_handler.handler_path
<< "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
<< "'";
@@ -236,28 +203,25 @@ void FirmwareHandler::ProcessFirmwareEvent(const std::string& root,
}
std::vector<std::string> attempted_paths_and_errors;
- auto TryLoadFirmware = [&](const std::string& firmware_directory) {
+
+ int booting = IsBooting();
+try_loading_again:
+ attempted_paths_and_errors.clear();
+ for (const auto& firmware_directory : firmware_directories_) {
std::string file = firmware_directory + firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
if (fw_fd == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", open failed: " + strerror(errno));
- return false;
+ continue;
}
struct stat sb;
if (fstat(fw_fd, &sb) == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", fstat failed: " + strerror(errno));
- return false;
+ continue;
}
LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
- return true;
- };
-
- int booting = IsBooting();
-try_loading_again:
- attempted_paths_and_errors.clear();
- if (ForEachFirmwareDirectory(TryLoadFirmware)) {
return;
}
@@ -278,33 +242,6 @@ try_loading_again:
write(loading_fd, "-1", 2);
}
-bool FirmwareHandler::ForEachFirmwareDirectory(
- std::function<bool(const std::string&)> handler) const {
- for (const std::string& firmware_directory : firmware_directories_) {
- if (std::invoke(handler, firmware_directory)) {
- return true;
- }
- }
-
- glob_t glob_result;
- glob("/apex/*/etc/firmware/", GLOB_MARK, nullptr, &glob_result);
- auto free_glob = android::base::make_scope_guard(std::bind(&globfree, &glob_result));
- for (size_t i = 0; i < glob_result.gl_pathc; i++) {
- char* apex_firmware_directory = glob_result.gl_pathv[i];
- // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
- // /apex/<name> paths, so unless we filter them out, we will look into the
- // same apex twice.
- if (strchr(apex_firmware_directory, '@')) {
- continue;
- }
- if (std::invoke(handler, apex_firmware_directory)) {
- return true;
- }
- }
-
- return false;
-}
-
void FirmwareHandler::HandleUevent(const Uevent& uevent) {
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 3c35b1f16..b4138f127 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -18,7 +18,6 @@
#include <pwd.h>
-#include <functional>
#include <string>
#include <vector>
@@ -30,13 +29,11 @@ namespace android {
namespace init {
struct ExternalFirmwareHandler {
- ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);
-
+ ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path)
+ : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {}
std::string devpath;
uid_t uid;
std::string handler_path;
-
- std::function<bool(const std::string&)> match;
};
class FirmwareHandler : public UeventHandler {
@@ -55,7 +52,6 @@ class FirmwareHandler : public UeventHandler {
const Uevent& uevent) const;
std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
- bool ForEachFirmwareDirectory(std::function<bool(const std::string&)> handler) const;
std::vector<std::string> firmware_directories_;
std::vector<ExternalFirmwareHandler> external_firmware_handlers_;
diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp
index f6e75b01f..7bb603c5d 100644
--- a/init/firmware_handler_test.cpp
+++ b/init/firmware_handler_test.cpp
@@ -79,8 +79,6 @@ TEST(firmware_handler, HandleAbort) {
}
int HandleAbort(int argc, char** argv) {
- // Since this is an expected failure, disable debuggerd to not generate a tombstone.
- signal(SIGABRT, SIG_DFL);
abort();
return 0;
}
@@ -103,23 +101,6 @@ int HandleBadPath(int argc, char** argv) {
return 0;
}
-TEST(firmware_handler, Matching) {
- ExternalFirmwareHandler h("/dev/path/a.bin", getuid(), "/test");
- ASSERT_TRUE(h.match("/dev/path/a.bin"));
- ASSERT_FALSE(h.match("/dev/path/a.bi"));
-
- h = ExternalFirmwareHandler("/dev/path/a.*", getuid(), "/test");
- ASSERT_TRUE(h.match("/dev/path/a.bin"));
- ASSERT_TRUE(h.match("/dev/path/a.bix"));
- ASSERT_FALSE(h.match("/dev/path/b.bin"));
-
- h = ExternalFirmwareHandler("/dev/*/a.bin", getuid(), "/test");
- ASSERT_TRUE(h.match("/dev/path/a.bin"));
- ASSERT_TRUE(h.match("/dev/other/a.bin"));
- ASSERT_FALSE(h.match("/dev/other/c.bin"));
- ASSERT_FALSE(h.match("/dev/path/b.bin"));
-}
-
} // namespace init
} // namespace android
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index e2ea0ab40..cfa0d99ee 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -30,41 +30,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-static bool KernelConsolePresent(const std::string& cmdline) {
- size_t pos = 0;
- while (true) {
- pos = cmdline.find("console=", pos);
- if (pos == std::string::npos) return false;
- if (pos == 0 || cmdline[pos - 1] == ' ') return true;
- pos++;
- }
-}
-
-static bool SetupConsole() {
- if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
- PLOG(ERROR) << "unable to create /dev/console";
- return false;
- }
- int fd = -1;
- int tries = 50; // should timeout after 5s
- // The device driver for console may not be ready yet so retry for a while in case of failure.
- while (tries--) {
- fd = open("/dev/console", O_RDWR);
- if (fd != -1) break;
- std::this_thread::sleep_for(100ms);
- }
- if (fd == -1) {
- PLOG(ERROR) << "could not open /dev/console";
- return false;
- }
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
- return true;
-}
-
static void RunScript() {
LOG(INFO) << "Attempting to run /first_stage.sh...";
pid_t pid = fork();
@@ -83,9 +48,11 @@ static void RunScript() {
namespace android {
namespace init {
-void StartConsole(const std::string& cmdline) {
- bool console = KernelConsolePresent(cmdline);
-
+void StartConsole() {
+ if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+ PLOG(ERROR) << "unable to create /dev/console";
+ return;
+ }
pid_t pid = fork();
if (pid != 0) {
int status;
@@ -93,32 +60,36 @@ void StartConsole(const std::string& cmdline) {
LOG(ERROR) << "console shell exited with status " << status;
return;
}
+ int fd = -1;
+ int tries = 50; // should timeout after 5s
+ // The device driver for console may not be ready yet so retry for a while in case of failure.
+ while (tries--) {
+ fd = open("/dev/console", O_RDWR);
+ if (fd != -1) {
+ break;
+ }
+ std::this_thread::sleep_for(100ms);
+ }
+ if (fd == -1) {
+ LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+ _exit(127);
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
- if (console) console = SetupConsole();
RunScript();
- if (console) {
- const char* path = "/system/bin/sh";
- const char* args[] = {path, nullptr};
- int rv = execv(path, const_cast<char**>(args));
- LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
- }
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
_exit(127);
}
-int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig) {
- auto pos = bootconfig.find("androidboot.first_stage_console =");
- if (pos != std::string::npos) {
- int val = 0;
- if (sscanf(bootconfig.c_str() + pos, "androidboot.first_stage_console = \"%d\"", &val) !=
- 1) {
- return FirstStageConsoleParam::DISABLED;
- }
- if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
- return val;
- }
- }
-
- pos = cmdline.find("androidboot.first_stage_console=");
+int FirstStageConsole(const std::string& cmdline) {
+ auto pos = cmdline.find("androidboot.first_stage_console=");
if (pos != std::string::npos) {
int val = 0;
if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index 4a30d35ed..8f36a7cbb 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -28,8 +28,8 @@ enum FirstStageConsoleParam {
MAX_PARAM_VALUE = IGNORE_FAILURE,
};
-void StartConsole(const std::string& cmdline);
-int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig);
+void StartConsole();
+int FirstStageConsole(const std::string& cmdline);
} // namespace init
} // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 78e5b60a1..021557697 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -41,8 +41,6 @@
#include "first_stage_console.h"
#include "first_stage_mount.h"
#include "reboot_utils.h"
-#include "second_stage_resources.h"
-#include "snapuserd_transition.h"
#include "switch_root.h"
#include "util.h"
@@ -91,20 +89,13 @@ void FreeRamdisk(DIR* dir, dev_t dev) {
}
}
}
- } else if (de->d_type == DT_REG) {
- // Do not free snapuserd if we will need the ramdisk copy during the
- // selinux transition.
- if (de->d_name == "snapuserd"s && IsFirstStageSnapuserdRunning()) {
- continue;
- }
}
unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
}
}
-bool ForceNormalBoot(const std::string& cmdline, const std::string& bootconfig) {
- return bootconfig.find("androidboot.force_normal_boot = \"1\"") != std::string::npos ||
- cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+bool ForceNormalBoot(const std::string& cmdline) {
+ return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
} // namespace
@@ -123,7 +114,7 @@ std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
}
#define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
+bool LoadKernelModules(bool recovery, bool want_console) {
struct utsname uts;
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
@@ -165,7 +156,7 @@ bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
dir_path.append(module_dir);
Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
bool retval = m.LoadListedModules(!want_console);
- modules_loaded = m.GetModuleCount();
+ int modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
return retval;
}
@@ -173,7 +164,7 @@ bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
bool retval = m.LoadListedModules(!want_console);
- modules_loaded = m.GetModuleCount();
+ int modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
return retval;
}
@@ -201,7 +192,6 @@ int FirstStageMain(int argc, char** argv) {
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
- CHECKCALL(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
@@ -210,10 +200,6 @@ int FirstStageMain(int argc, char** argv) {
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
- // Don't expose the raw bootconfig to unprivileged processes.
- chmod("/proc/bootconfig", 0440);
- std::string bootconfig;
- android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
gid_t groups[] = {AID_READPROC};
CHECKCALL(setgroups(arraysize(groups), groups));
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
@@ -249,11 +235,6 @@ int FirstStageMain(int argc, char** argv) {
// /debug_ramdisk is used to preserve additional files from the debug ramdisk
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
-
- // /second_stage_resources is used to preserve files from first to second
- // stage init
- CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=0"))
#undef CHECKCALL
SetStdioToDevNull(argv);
@@ -281,54 +262,32 @@ int FirstStageMain(int argc, char** argv) {
old_root_dir.reset();
}
- auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
+ auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
- boot_clock::time_point module_start_time = boot_clock::now();
- int module_count = 0;
- if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
- module_count)) {
+ if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
- if (module_count > 0) {
- auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(
- boot_clock::now() - module_start_time);
- setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
- LOG(INFO) << "Loaded " << module_count << " kernel modules took "
- << module_elapse_time.count() << " ms";
- }
-
- bool created_devices = false;
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
- if (!IsRecoveryMode()) {
- created_devices = DoCreateDevices();
- if (!created_devices){
- LOG(ERROR) << "Failed to create device nodes early";
- }
- }
- StartConsole(cmdline);
+ StartConsole();
}
- if (access(kBootImageRamdiskProp, F_OK) == 0) {
- std::string dest = GetRamdiskPropForSecondStage();
- std::string dir = android::base::Dirname(dest);
- std::error_code ec;
- if (!fs::create_directories(dir, ec) && !!ec) {
- LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
- }
- if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {
- LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "
- << ec.message();
+ if (ForceNormalBoot(cmdline)) {
+ mkdir("/first_stage_ramdisk", 0755);
+ // SwitchRoot() must be called with a mount point as the target, so we bind mount the
+ // target directory to itself here.
+ if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
+ LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
- LOG(INFO) << "Copied ramdisk prop to " << dest;
+ SwitchRoot("/first_stage_ramdisk");
}
- // If "/force_debuggable" is present, the second-stage init will use a userdebug
- // sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
+ // If this file is present, the second-stage init will use a userdebug sepolicy
+ // and load adb_debug.prop to allow adb root, if the device is unlocked.
if (access("/force_debuggable", F_OK) == 0) {
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
@@ -340,17 +299,7 @@ int FirstStageMain(int argc, char** argv) {
}
}
- if (ForceNormalBoot(cmdline, bootconfig)) {
- mkdir("/first_stage_ramdisk", 0755);
- // SwitchRoot() must be called with a mount point as the target, so we bind mount the
- // target directory to itself here.
- if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
- LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
- }
- SwitchRoot("/first_stage_ramdisk");
- }
-
- if (!DoFirstStageMount(!created_devices)) {
+ if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
index 1211f29b7..7de816f2a 100644
--- a/init/first_stage_init.h
+++ b/init/first_stage_init.h
@@ -22,7 +22,6 @@ namespace init {
int FirstStageMain(int argc, char** argv);
static constexpr char kEnvFirstStageStartedAt[] = "FIRST_STAGE_STARTED_AT";
-static constexpr char kEnvInitModuleDurationMs[] = "INIT_MODULE_DURATION_MS";
} // namespace init
} // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index f5c10bbd4..8eb2f9748 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -44,15 +44,12 @@
#include "block_dev_initializer.h"
#include "devices.h"
-#include "result.h"
-#include "snapuserd_transition.h"
#include "switch_root.h"
#include "uevent.h"
#include "uevent_listener.h"
#include "util.h"
using android::base::ReadFileToString;
-using android::base::Result;
using android::base::Split;
using android::base::StringPrintf;
using android::base::Timer;
@@ -83,20 +80,19 @@ class FirstStageMount {
// The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
// based on device tree configurations.
- static Result<std::unique_ptr<FirstStageMount>> Create();
- bool DoCreateDevices(); // Creates devices and logical partitions from storage devices
+ static std::unique_ptr<FirstStageMount> Create();
bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
bool InitDevices();
protected:
bool InitRequiredDevices(std::set<std::string> devices);
bool CreateLogicalPartitions();
- bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm);
bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end = nullptr);
bool MountPartitions();
bool TrySwitchSystemAsRoot();
+ bool TrySkipMountingPartitions();
bool IsDmLinearEnabled();
void GetSuperDeviceName(std::set<std::string>* devices);
bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
@@ -113,7 +109,6 @@ class FirstStageMount {
bool need_dm_verity_;
bool dsu_not_on_userdata_ = false;
- bool use_snapuserd_ = false;
Fstab fstab_;
// The super path is only set after InitDevices, and is invalid before.
@@ -161,7 +156,7 @@ static inline bool IsDtVbmetaCompatible(const Fstab& fstab) {
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
-static Result<Fstab> ReadFirstStageFstab() {
+static Fstab ReadFirstStageFstab() {
Fstab fstab;
if (!ReadFstabFromDt(&fstab)) {
if (ReadDefaultFstab(&fstab)) {
@@ -171,7 +166,7 @@ static Result<Fstab> ReadFirstStageFstab() {
}),
fstab.end());
} else {
- return Error() << "failed to read default fstab for first stage mount";
+ LOG(INFO) << "Failed to fstab for first stage mount";
}
}
return fstab;
@@ -237,41 +232,15 @@ FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(s
super_partition_name_ = fs_mgr_get_super_partition_name();
}
-Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create() {
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
auto fstab = ReadFirstStageFstab();
- if (!fstab.ok()) {
- return fstab.error();
- }
-
- if (IsDtVbmetaCompatible(*fstab)) {
- return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
+ if (IsDtVbmetaCompatible(fstab)) {
+ return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
} else {
- return std::make_unique<FirstStageMountVBootV1>(std::move(*fstab));
+ return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
}
}
-bool FirstStageMount::DoCreateDevices() {
- if (!InitDevices()) return false;
-
- // Mount /metadata before creating logical partitions, since we need to
- // know whether a snapshot merge is in progress.
- auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
- return entry.mount_point == "/metadata";
- });
- if (metadata_partition != fstab_.end()) {
- if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
- // Copies DSU AVB keys from the ramdisk to /metadata.
- // Must be done before the following TrySwitchSystemAsRoot().
- // Otherwise, ramdisk will be inaccessible after switching root.
- CopyDsuAvbKeys();
- }
- }
-
- if (!CreateLogicalPartitions()) return false;
-
- return true;
-}
-
bool FirstStageMount::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
@@ -279,6 +248,8 @@ bool FirstStageMount::DoFirstStageMount() {
return true;
}
+ if (!InitDevices()) return false;
+
if (!MountPartitions()) return false;
return true;
@@ -367,7 +338,12 @@ bool FirstStageMount::CreateLogicalPartitions() {
return false;
}
if (sm->NeedSnapshotsInFirstStageMount()) {
- return CreateSnapshotPartitions(sm.get());
+ // When COW images are present for snapshots, they are stored on
+ // the data partition.
+ if (!InitRequiredDevices({"userdata"})) {
+ return false;
+ }
+ return sm->CreateLogicalAndSnapshotPartitions(super_path_);
}
}
@@ -382,37 +358,6 @@ bool FirstStageMount::CreateLogicalPartitions() {
return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}
-bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) {
- // When COW images are present for snapshots, they are stored on
- // the data partition.
- if (!InitRequiredDevices({"userdata"})) {
- return false;
- }
-
- use_snapuserd_ = sm->IsSnapuserdRequired();
- if (use_snapuserd_) {
- LaunchFirstStageSnapuserd();
- }
-
- sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
- if (android::base::StartsWith(device, "/dev/block/dm-")) {
- return block_dev_init_.InitDmDevice(device);
- }
- if (android::base::StartsWith(device, "/dev/dm-user/")) {
- return block_dev_init_.InitDmUser(android::base::Basename(device));
- }
- return block_dev_init_.InitDevices({device});
- });
- if (!sm->CreateLogicalAndSnapshotPartitions(super_path_)) {
- return false;
- }
-
- if (use_snapuserd_) {
- CleanupSnapuserdSocket();
- }
- return true;
-}
-
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end) {
// Sets end to begin + 1, so we can just return on failure below.
@@ -420,10 +365,6 @@ bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_sa
*end = begin + 1;
}
- if (!fs_mgr_create_canonical_mount_point(begin->mount_point)) {
- return false;
- }
-
if (begin->fs_mgr_flags.logical) {
if (!fs_mgr_update_logical_partition(&(*begin))) {
return false;
@@ -516,10 +457,6 @@ bool FirstStageMount::TrySwitchSystemAsRoot() {
if (system_partition == fstab_.end()) return true;
- if (use_snapuserd_) {
- SaveRamdiskPathToSnapuserd();
- }
-
if (MountPartition(system_partition, false /* erase_same_mounts */)) {
if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
LOG(ERROR) << "check_most_at_once forbidden on external media";
@@ -535,9 +472,25 @@ bool FirstStageMount::TrySwitchSystemAsRoot() {
}
bool FirstStageMount::MountPartitions() {
+ // Mount /metadata before creating logical partitions, since we need to
+ // know whether a snapshot merge is in progress.
+ auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+ return entry.mount_point == "/metadata";
+ });
+ if (metadata_partition != fstab_.end()) {
+ if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
+ // Copies DSU AVB keys from the ramdisk to /metadata.
+ // Must be done before the following TrySwitchSystemAsRoot().
+ // Otherwise, ramdisk will be inaccessible after switching root.
+ CopyDsuAvbKeys();
+ }
+ }
+
+ if (!CreateLogicalPartitions()) return false;
+
if (!TrySwitchSystemAsRoot()) return false;
- if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
+ if (!SkipMountingPartitions(&fstab_)) return false;
for (auto current = fstab_.begin(); current != fstab_.end();) {
// We've already mounted /system above.
@@ -546,12 +499,6 @@ bool FirstStageMount::MountPartitions() {
continue;
}
- // Handle overlayfs entries later.
- if (current->fs_type == "overlay") {
- ++current;
- continue;
- }
-
// Skip raw partition entries such as boot, dtbo, etc.
// Having emmc fstab entries allows us to probe current->vbmeta_partition
// in InitDevices() when they are AVB chained partitions.
@@ -576,12 +523,6 @@ bool FirstStageMount::MountPartitions() {
current = end;
}
- for (const auto& entry : fstab_) {
- if (entry.fs_type == "overlay") {
- fs_mgr_mount_overlayfs_fstab_entry(entry);
- }
- }
-
// If we don't see /system or / in the fstab, then we need to create an root entry for
// overlayfs.
if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
@@ -672,7 +613,7 @@ void FirstStageMount::UseDsuIfPresent() {
}
// Publish the logical partition names for TransformFstabForDsu
WriteFile(gsi::kGsiLpNamesFile, lp_names);
- TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
+ TransformFstabForDsu(&fstab_, dsu_partitions);
}
bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
@@ -693,10 +634,6 @@ bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices)
// Includes the partition names of fstab records.
// Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
for (const auto& fstab_entry : fstab_) {
- // Skip pseudo filesystems.
- if (fstab_entry.fs_type == "overlay") {
- continue;
- }
if (!fstab_entry.fs_mgr_flags.logical) {
devices->emplace(basename(fstab_entry.blk_device.c_str()));
}
@@ -759,10 +696,6 @@ bool FirstStageMountVBootV2::GetDmVerityDevices(std::set<std::string>* devices)
if (fstab_entry.fs_mgr_flags.avb) {
need_dm_verity_ = true;
}
- // Skip pseudo filesystems.
- if (fstab_entry.fs_type == "overlay") {
- continue;
- }
if (fstab_entry.fs_mgr_flags.logical) {
// Don't try to find logical partitions via uevent regeneration.
logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
@@ -863,35 +796,20 @@ bool FirstStageMountVBootV2::InitAvbHandle() {
// Public functions
// ----------------
-// Creates devices and logical partitions from storage devices
-bool DoCreateDevices() {
- auto fsm = FirstStageMount::Create();
- if (!fsm.ok()) {
- LOG(ERROR) << "Failed to create FirstStageMount: " << fsm.error();
- return false;
- }
- return (*fsm)->DoCreateDevices();
-}
-
// Mounts partitions specified by fstab in device tree.
-bool DoFirstStageMount(bool create_devices) {
+bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
LOG(INFO) << "First stage mount skipped (recovery mode)";
return true;
}
- auto fsm = FirstStageMount::Create();
- if (!fsm.ok()) {
- LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
+ std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+ if (!handle) {
+ LOG(ERROR) << "Failed to create FirstStageMount";
return false;
}
-
- if (create_devices) {
- if (!(*fsm)->DoCreateDevices()) return false;
- }
-
- return (*fsm)->DoFirstStageMount();
+ return handle->DoFirstStageMount();
}
void SetInitAvbVersionInRecovery() {
@@ -901,12 +819,8 @@ void SetInitAvbVersionInRecovery() {
}
auto fstab = ReadFirstStageFstab();
- if (!fstab.ok()) {
- LOG(ERROR) << fstab.error();
- return;
- }
- if (!IsDtVbmetaCompatible(*fstab)) {
+ if (!IsDtVbmetaCompatible(fstab)) {
LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
return;
}
@@ -916,7 +830,7 @@ void SetInitAvbVersionInRecovery() {
// We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
// Open() function returns a valid handle.
// We don't need to mount partitions here in recovery mode.
- FirstStageMountVBootV2 avb_first_mount(std::move(*fstab));
+ FirstStageMountVBootV2 avb_first_mount(std::move(fstab));
if (!avb_first_mount.InitDevices()) {
LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
return;
diff --git a/init/first_stage_mount.h b/init/first_stage_mount.h
index 2f4e66382..21d87fd48 100644
--- a/init/first_stage_mount.h
+++ b/init/first_stage_mount.h
@@ -19,8 +19,7 @@
namespace android {
namespace init {
-bool DoCreateDevices();
-bool DoFirstStageMount(bool create_devices);
+bool DoFirstStageMount();
void SetInitAvbVersionInRecovery();
} // namespace init
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 2a8bf6c2b..caa8f8d0d 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -20,7 +20,6 @@
#include <sys/socket.h>
#include <sys/types.h>
-#include <optional>
#include <string>
#include <android-base/properties.h>
@@ -42,7 +41,7 @@ inline bool CanReadProperty(const std::string&, const std::string&) {
}
// reboot_utils.h
-inline void SetFatalRebootTarget(const std::optional<std::string>& = std::nullopt) {}
+inline void SetFatalRebootTarget() {}
inline void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
abort();
}
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index db127d3f2..0bd4df463 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -25,8 +25,6 @@
#include <fstream>
#include <iostream>
#include <iterator>
-#include <map>
-#include <set>
#include <string>
#include <vector>
@@ -34,7 +32,6 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
-#include <generated_android_ids.h>
#include <hidl/metadata.h>
#include <property_info_serializer/property_info_serializer.h>
@@ -51,9 +48,11 @@
#include "service_list.h"
#include "service_parser.h"
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
using namespace std::literals;
-using android::base::EndsWith;
using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Split;
@@ -64,10 +63,6 @@ using android::properties::PropertyInfoEntry;
static std::vector<std::string> passwd_files;
-// NOTE: Keep this in sync with the order used by init.cpp LoadBootScripts()
-static const std::vector<std::string> partition_search_order =
- std::vector<std::string>({"system", "system_ext", "odm", "vendor", "product"});
-
static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
std::string passwd;
if (!ReadFileToString(passwd_file, &passwd)) {
@@ -155,24 +150,13 @@ static Result<void> check_stub(const BuiltinArguments& args) {
#include "generated_stub_builtin_function_map.h"
void PrintUsage() {
- fprintf(stdout, R"(usage: host_init_verifier [options]
-
-Tests init script(s) for correctness.
-
-Generic options:
- -p FILE Search this passwd file for users and groups.
- --property_contexts=FILE Use this file for property_contexts.
-
-Single script mode options:
- [init rc file] Positional argument; test this init script.
-
-Multiple script mode options:
- --out_system=DIR Path to the output product directory for the system partition.
- --out_system_ext=DIR Path to the output product directory for the system_ext partition.
- --out_odm=DIR Path to the output product directory for the odm partition.
- --out_vendor=DIR Path to the output product directory for the vendor partition.
- --out_product=DIR Path to the output product directory for the product partition.
-)");
+ std::cout << "usage: host_init_verifier [options] <init rc file>\n"
+ "\n"
+ "Tests an init script for correctness\n"
+ "\n"
+ "-p FILE\tSearch this passwd file for users and groups\n"
+ "--property_contexts=FILE\t Use this file for property_contexts\n"
+ << std::endl;
}
Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
@@ -221,18 +205,12 @@ int main(int argc, char** argv) {
android::base::SetMinimumLogSeverity(android::base::ERROR);
auto property_infos = std::vector<PropertyInfoEntry>();
- std::map<std::string, std::string> partition_map;
while (true) {
static const char kPropertyContexts[] = "property-contexts=";
static const struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{kPropertyContexts, required_argument, nullptr, 0},
- {"out_system", required_argument, nullptr, 0},
- {"out_system_ext", required_argument, nullptr, 0},
- {"out_odm", required_argument, nullptr, 0},
- {"out_vendor", required_argument, nullptr, 0},
- {"out_product", required_argument, nullptr, 0},
{nullptr, 0, nullptr, 0},
};
@@ -248,16 +226,6 @@ int main(int argc, char** argv) {
if (long_options[option_index].name == kPropertyContexts) {
HandlePropertyContexts(optarg, &property_infos);
}
- for (const auto& p : partition_search_order) {
- if (long_options[option_index].name == "out_" + p) {
- if (partition_map.find(p) != partition_map.end()) {
- PrintUsage();
- return EXIT_FAILURE;
- }
- partition_map[p] =
- EndsWith(optarg, "/") ? optarg : std::string(optarg) + "/";
- }
- }
break;
case 'h':
PrintUsage();
@@ -274,9 +242,7 @@ int main(int argc, char** argv) {
argc -= optind;
argv += optind;
- // If provided, use the partition map to check multiple init rc files.
- // Otherwise, check a single init rc file.
- if ((!partition_map.empty() && argc != 0) || (partition_map.empty() && argc != 1)) {
+ if (argc != 1) {
PrintUsage();
return EXIT_FAILURE;
}
@@ -298,42 +264,24 @@ int main(int argc, char** argv) {
property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());
- if (!partition_map.empty()) {
- std::vector<std::string> vendor_prefixes;
- for (const auto& partition : {"vendor", "odm"}) {
- if (partition_map.find(partition) != partition_map.end()) {
- vendor_prefixes.push_back(partition_map.at(partition));
- }
- }
- InitializeHostSubcontext(vendor_prefixes);
- }
-
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&sl, GetSubcontext(),
- *interface_inheritance_hierarchy_map));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(
+ &sl, nullptr, *interface_inheritance_hierarchy_map));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!partition_map.empty()) {
- for (const auto& p : partition_search_order) {
- if (partition_map.find(p) != partition_map.end()) {
- parser.ParseConfig(partition_map.at(p) + "etc/init");
- }
- }
- } else {
- if (!parser.ParseConfigFileInsecure(*argv)) {
- LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
- return EXIT_FAILURE;
- }
+ if (!parser.ParseConfigFileInsecure(*argv)) {
+ LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
+ return EXIT_FAILURE;
}
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
if (failures > 0) {
- LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
+ LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
+ << " errors";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/init/init.cpp b/init/init.cpp
index a7325cad9..29859c5f5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,7 +18,6 @@
#include <dirent.h>
#include <fcntl.h>
-#include <paths.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
@@ -47,13 +46,11 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
#include <libavb/libavb.h>
#include <libgsi/libgsi.h>
-#include <libsnapshot/snapshot.h>
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
#include <selinux/android.h>
@@ -72,14 +69,12 @@
#include "proto_utils.h"
#include "reboot.h"
#include "reboot_utils.h"
-#include "second_stage_resources.h"
#include "security.h"
#include "selabel.h"
#include "selinux.h"
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
-#include "snapuserd_transition.h"
#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"
@@ -96,7 +91,6 @@ using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
using android::fs_mgr::AvbHandle;
-using android::snapshot::SnapshotManager;
namespace android {
namespace init {
@@ -237,43 +231,18 @@ static class ShutdownState {
std::optional<std::string> CheckShutdown() {
auto lock = std::lock_guard{shutdown_command_lock_};
if (do_shutdown_ && !IsShuttingDown()) {
+ do_shutdown_ = false;
return shutdown_command_;
}
return {};
}
- bool do_shutdown() const { return do_shutdown_; }
- void set_do_shutdown(bool value) { do_shutdown_ = value; }
-
private:
std::mutex shutdown_command_lock_;
std::string shutdown_command_;
bool do_shutdown_ = false;
} shutdown_state;
-static void UnwindMainThreadStack() {
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
- }
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
- }
-}
-
-void DebugRebootLogging() {
- LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
- << " IsShuttingDown: " << IsShuttingDown();
- if (shutdown_state.do_shutdown()) {
- LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
- UnwindMainThreadStack();
- }
- if (IsShuttingDown()) {
- LOG(ERROR) << "sys.powerctl set while init is already shutting down";
- UnwindMainThreadStack();
- }
-}
-
void DumpState() {
ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
@@ -312,14 +281,14 @@ static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
- if (!parser.ParseConfig("/vendor/etc/init")) {
- late_import_paths.emplace_back("/vendor/etc/init");
+ if (!parser.ParseConfig("/product/etc/init")) {
+ late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
- if (!parser.ParseConfig("/product/etc/init")) {
- late_import_paths.emplace_back("/product/etc/init");
+ if (!parser.ParseConfig("/vendor/etc/init")) {
+ late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
@@ -518,9 +487,11 @@ static void export_oem_lock_status() {
if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
return;
}
- SetProperty(
- "ro.boot.flash.locked",
- android::base::GetProperty("ro.boot.verifiedbootstate", "") == "orange" ? "0" : "1");
+ ImportKernelCmdline([](const std::string& key, const std::string& value) {
+ if (key == "androidboot.verifiedbootstate") {
+ SetProperty("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+ }
+ });
}
static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
@@ -668,12 +639,6 @@ static void UmountDebugRamdisk() {
}
}
-static void UmountSecondStageRes() {
- if (umount(kSecondStageRes) != 0) {
- PLOG(ERROR) << "Failed to umount " << kSecondStageRes;
- }
-}
-
static void MountExtraFilesystems() {
#define CHECKCALL(x) \
if ((x) != 0) PLOG(FATAL) << #x " failed.";
@@ -711,10 +676,6 @@ static void RecordStageBoottimes(const boot_clock::time_point& second_stage_star
SetProperty("ro.boottime.init.selinux",
std::to_string(second_stage_start_time.time_since_epoch().count() -
selinux_start_time_ns));
- if (auto init_module_time_str = getenv(kEnvInitModuleDurationMs); init_module_time_str) {
- SetProperty("ro.boottime.init.modules", init_module_time_str);
- unsetenv(kEnvInitModuleDurationMs);
- }
}
void SendLoadPersistentPropertiesMessage() {
@@ -738,12 +699,6 @@ int SecondStageMain(int argc, char** argv) {
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
- // Update $PATH in the case the second stage init is newer than first stage init, where it is
- // first set.
- if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
- PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
- }
-
// Init should not crash because of a dependence on any other process, therefore we ignore
// SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
// is inherited across exec, but custom signal handlers are not. Since we do not want to
@@ -786,9 +741,6 @@ int SecondStageMain(int argc, char** argv) {
PropertyInit();
- // Umount second stage resources after property service has read the .prop files.
- UmountSecondStageRes();
-
// Umount the debug ramdisk after property service has read the .prop files when it means to.
if (load_debug_prop) {
UmountDebugRamdisk();
@@ -857,6 +809,7 @@ int SecondStageMain(int argc, char** argv) {
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
Keychords keychords;
am.QueueBuiltinAction(
@@ -872,6 +825,10 @@ int SecondStageMain(int argc, char** argv) {
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
+ // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
+ // wasn't ready immediately after wait_for_coldboot_done
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
@@ -883,18 +840,13 @@ int SecondStageMain(int argc, char** argv) {
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
- // Restore prio before main loop
- setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
- LOG(INFO) << "Got shutdown_command '" << *shutdown_command
- << "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
- shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
diff --git a/init/init.h b/init/init.h
index 4f686cb5b..27f64e297 100644
--- a/init/init.h
+++ b/init/init.h
@@ -42,8 +42,6 @@ void SendLoadPersistentPropertiesMessage();
void PropertyChanged(const std::string& name, const std::string& value);
bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
-void DebugRebootLogging();
-
int SecondStageMain(int argc, char** argv);
} // namespace init
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 8550ec8f3..fa6574026 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,7 +17,6 @@
#include <functional>
#include <android-base/file.h>
-#include <android-base/logging.h>
#include <android-base/properties.h>
#include <gtest/gtest.h>
@@ -269,17 +268,6 @@ service A something
ASSERT_EQ(1u, parser.parse_error_count());
}
-class TestCaseLogger : public ::testing::EmptyTestEventListener {
- void OnTestStart(const ::testing::TestInfo& test_info) override {
-#ifdef __ANDROID__
- LOG(INFO) << "===== " << test_info.test_suite_name() << "::" << test_info.name() << " ("
- << test_info.file() << ":" << test_info.line() << ")";
-#else
- UNUSED(test_info);
-#endif
- }
-};
-
} // namespace init
} // namespace android
@@ -296,6 +284,5 @@ int main(int argc, char** argv) {
}
testing::InitGoogleTest(&argc, argv);
- testing::UnitTest::GetInstance()->listeners().Append(new android::init::TestCaseLogger());
return RUN_ALL_TESTS();
}
diff --git a/init/main.cpp b/init/main.cpp
index 23f5530fe..38bc74b62 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -52,8 +52,7 @@ int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
- // Boost prio which will be restored later
- setpriority(PRIO_PROCESS, 0, -20);
+
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 46f833104..01abba8d1 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -130,11 +130,7 @@ void MountHandler::MountHandlerFunction() {
char* buf = nullptr;
size_t len = 0;
while (getline(&buf, &len, fp_.get()) != -1) {
- auto buf_string = std::string(buf);
- if (buf_string.find("/emulated") != std::string::npos) {
- continue;
- }
- auto entry = ParseMount(buf_string);
+ auto entry = ParseMount(std::string(buf));
auto match = untouched.find(entry);
if (match == untouched.end()) {
touched.emplace_back(std::move(entry));
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 2a578082b..f3b584c4c 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,34 +27,58 @@
#include <android-base/properties.h>
#include <android-base/result.h>
#include <android-base/unique_fd.h>
+#include <apex_manifest.pb.h>
#include "util.h"
-#ifndef RECOVERY
-#define ACTIVATE_FLATTENED_APEX 1
-#endif
-
-#ifdef ACTIVATE_FLATTENED_APEX
-#include <apex_manifest.pb.h>
-#include <com_android_apex.h>
-#include <selinux/android.h>
-#endif // ACTIVATE_FLATTENED_APEX
-
namespace android {
namespace init {
namespace {
-static bool BindMount(const std::string& source, const std::string& mount_point) {
- if (mount(source.c_str(), mount_point.c_str(), nullptr, MS_BIND | MS_REC, nullptr) == -1) {
+static bool BindMount(const std::string& source, const std::string& mount_point,
+ bool recursive = false) {
+ unsigned long mountflags = MS_BIND;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
PLOG(ERROR) << "Failed to bind mount " << source;
return false;
}
return true;
}
-static bool ChangeMount(const std::string& mount_point, unsigned long mountflags) {
+static bool MakeShared(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_SHARED;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to shared";
+ return false;
+ }
+ return true;
+}
+
+static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_SLAVE;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to slave";
+ return false;
+ }
+ return true;
+}
+
+static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_PRIVATE;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to remount " << mount_point << " as " << std::hex << mountflags;
+ PLOG(ERROR) << "Failed to change propagation type to private";
return false;
}
return true;
@@ -82,8 +106,6 @@ static bool IsApexUpdatable() {
return updatable;
}
-#ifdef ACTIVATE_FLATTENED_APEX
-
static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
return ErrnoError() << "Could not create mount point " << mount_path;
@@ -94,7 +116,7 @@ static Result<void> MountDir(const std::string& path, const std::string& mount_p
return {};
}
-static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
+static Result<std::string> GetApexName(const std::string& apex_dir) {
const std::string manifest_path = apex_dir + "/apex_manifest.pb";
std::string content;
if (!android::base::ReadFileToString(manifest_path, &content)) {
@@ -104,40 +126,31 @@ static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex
if (!manifest.ParseFromString(content)) {
return Error() << "Can't parse manifest file: " << manifest_path;
}
- return manifest;
+ return manifest.name();
}
-template <typename Fn>
static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
- const std::string& to_dir, Fn on_activate) {
+ const std::string& to_dir) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
if (!dir) {
return {};
}
dirent* entry;
- std::vector<std::string> entries;
-
while ((entry = readdir(dir.get())) != nullptr) {
if (entry->d_name[0] == '.') continue;
if (entry->d_type == DT_DIR) {
- entries.push_back(entry->d_name);
+ const std::string apex_path = from_dir + "/" + entry->d_name;
+ const auto apex_name = GetApexName(apex_path);
+ if (!apex_name.ok()) {
+ LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+ continue;
+ }
+ const std::string mount_path = to_dir + "/" + (*apex_name);
+ if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
+ return result;
+ }
}
}
-
- std::sort(entries.begin(), entries.end());
- for (const auto& name : entries) {
- const std::string apex_path = from_dir + "/" + name;
- const auto apex_manifest = GetApexManifest(apex_path);
- if (!apex_manifest.ok()) {
- LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
- continue;
- }
- const std::string mount_path = to_dir + "/" + apex_manifest->name();
- if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
- return result;
- }
- on_activate(apex_path, *apex_manifest);
- }
return {};
}
@@ -154,38 +167,29 @@ static bool ActivateFlattenedApexesIfPossible() {
"/vendor/apex",
};
- std::vector<com::android::apex::ApexInfo> apex_infos;
- auto on_activate = [&](const std::string& apex_path,
- const apex::proto::ApexManifest& apex_manifest) {
- apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
- apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true,
- /* lastUpdateMillis= */ 0);
- };
-
for (const auto& dir : kBuiltinDirsForApexes) {
- if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
+ if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result.ok()) {
LOG(ERROR) << result.error();
return false;
}
}
+ return true;
+}
- std::ostringstream oss;
- com::android::apex::ApexInfoList apex_info_list(apex_infos);
- com::android::apex::write(oss, apex_info_list);
- const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
- if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
- PLOG(ERROR) << "Failed to write " << kApexInfoList;
- return false;
+static Result<void> MountLinkerConfigForDefaultNamespace() {
+ // No need to mount linkerconfig for default mount namespace if the path does not exist (which
+ // would mean it is already mounted)
+ if (access("/linkerconfig/default", 0) != 0) {
+ return {};
}
- if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
- PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
+
+ if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
+ return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
}
- return true;
+ return {};
}
-#endif // ACTIVATE_FLATTENED_APEX
-
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -200,17 +204,17 @@ bool SetupMountNamespaces() {
// needed for /foo/bar, then we will make /foo/bar as a mount point (by
// bind-mounting by to itself) and set the propagation type of the mount
// point to private.
- if (!ChangeMount("/", MS_SHARED | MS_REC)) return false;
+ if (!MakeShared("/", true /*recursive*/)) return false;
// /apex is a private mountpoint to give different sets of APEXes for
// the bootstrap and default mount namespaces. The processes running with
// the bootstrap namespace get APEXes from the read-only partition.
- if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
+ if (!(MakePrivate("/apex"))) return false;
// /linkerconfig is a private mountpoint to give a different linker configuration
// based on the mount namespace. Subdirectory will be bind-mounted based on current mount
// namespace
- if (!(ChangeMount("/linkerconfig", MS_PRIVATE))) return false;
+ if (!(MakePrivate("/linkerconfig"))) return false;
// The two mount namespaces present challenges for scoped storage, because
// vold, which is responsible for most of the mounting, lives in the
@@ -241,15 +245,15 @@ bool SetupMountNamespaces() {
if (!mkdir_recursive("/mnt/user", 0755)) return false;
if (!mkdir_recursive("/mnt/installer", 0755)) return false;
if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
- if (!(BindMount("/mnt/user", "/mnt/installer"))) return false;
- if (!(BindMount("/mnt/user", "/mnt/androidwritable"))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
// First, make /mnt/installer and /mnt/androidwritable a slave bind mount
- if (!(ChangeMount("/mnt/installer", MS_SLAVE))) return false;
- if (!(ChangeMount("/mnt/androidwritable", MS_SLAVE))) return false;
+ if (!(MakeSlave("/mnt/installer"))) return false;
+ if (!(MakeSlave("/mnt/androidwritable"))) return false;
// Then, make it shared again - effectively creating a new peer group, that
// will be inherited by new mount namespaces.
- if (!(ChangeMount("/mnt/installer", MS_SHARED))) return false;
- if (!(ChangeMount("/mnt/androidwritable", MS_SHARED))) return false;
+ if (!(MakeShared("/mnt/installer"))) return false;
+ if (!(MakeShared("/mnt/androidwritable"))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
@@ -279,42 +283,47 @@ bool SetupMountNamespaces() {
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
}
-#ifdef ACTIVATE_FLATTENED_APEX
+
success &= ActivateFlattenedApexesIfPossible();
-#endif
+
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
-Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
- if (IsRecoveryMode() || !IsApexUpdatable()) {
- // we don't have multiple namespaces in recovery mode or if apex is not updatable
- return {};
+bool SwitchToDefaultMountNamespace() {
+ if (IsRecoveryMode()) {
+ // we don't have multiple namespaces in recovery mode
+ return true;
}
- const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
- const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
- const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
- if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
- if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
- return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
+ if (default_ns_id != GetMountNamespaceId()) {
+ if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
+ return false;
+ }
+
+ if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
+ LOG(ERROR) << result.error();
+ return false;
}
}
- return {};
+
+ LOG(INFO) << "Switched to default mount namespace";
+ return true;
}
-base::Result<MountNamespace> GetCurrentMountNamespace() {
- std::string current_namespace_id = GetMountNamespaceId();
- if (current_namespace_id == "") {
- return Error() << "Failed to get current mount namespace ID";
+bool SwitchToBootstrapMountNamespaceIfNeeded() {
+ if (IsRecoveryMode()) {
+ // we don't have multiple namespaces in recovery mode
+ return true;
}
-
- if (current_namespace_id == bootstrap_ns_id) {
- return NS_BOOTSTRAP;
- } else if (current_namespace_id == default_ns_id) {
- return NS_DEFAULT;
+ if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
+ IsApexUpdatable()) {
+ if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
+ return false;
+ }
}
-
- return Error() << "Failed to find current mount namespace";
+ return true;
}
} // namespace init
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index 5e3dab241..c41a449f3 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -16,17 +16,12 @@
#pragma once
-#include <android-base/result.h>
-
namespace android {
namespace init {
-enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
-
bool SetupMountNamespaces();
-base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
-
-base::Result<MountNamespace> GetCurrentMountNamespace();
+bool SwitchToDefaultMountNamespace();
+bool SwitchToBootstrapMountNamespaceIfNeeded();
} // namespace init
} // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 5c18551e1..507ee4a5b 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -41,7 +41,7 @@ void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callbac
}
void Parser::ParseData(const std::string& filename, std::string* data) {
- data->push_back('\n');
+ data->push_back('\n'); // TODO: fix tokenizer
data->push_back('\0');
parse_state state;
diff --git a/init/perfboot.py b/init/perfboot.py
index 4b23ad28c..713290bcd 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -349,9 +349,9 @@ def print_summary(record_list, end_tag):
# Filter out invalid data.
end_times = [get_last_value(record, end_tag) for record in record_list
if get_last_value(record, end_tag) != 0]
- print 'mean:', int(round(mean(end_times))), 'ms'
- print 'median:', int(round(median(end_times))), 'ms'
- print 'standard deviation:', int(round(stddev(end_times))), 'ms'
+ print 'mean: ', mean(end_times)
+ print 'median:', median(end_times)
+ print 'standard deviation:', stddev(end_times)
def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 2d67bf5d7..a89504e59 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -44,7 +44,6 @@
#include <mutex>
#include <optional>
#include <queue>
-#include <string_view>
#include <thread>
#include <vector>
@@ -52,7 +51,6 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -68,7 +66,6 @@
#include "persistent_properties.h"
#include "property_type.h"
#include "proto_utils.h"
-#include "second_stage_resources.h"
#include "selinux.h"
#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
@@ -77,7 +74,6 @@
using namespace std::literals;
using android::base::GetProperty;
-using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -94,12 +90,6 @@ using android::sysprop::InitProperties::is_userspace_reboot_supported;
namespace android {
namespace init {
-constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint";
-constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint";
-constexpr auto ID_PROP = "ro.build.id";
-constexpr auto LEGACY_ID_PROP = "ro.build.legacy.id";
-constexpr auto VBMETA_DIGEST_PROP = "ro.boot.vbmeta.digest";
-constexpr auto DIGEST_SIZE_USED = 8;
static bool persistent_properties_loaded = false;
@@ -501,9 +491,6 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value,
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
- if (!value.empty()) {
- DebugRebootLogging();
- }
if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
*error = "Userspace reboot is not supported by this device";
return PROP_ERROR_INVALID_VALUE;
@@ -640,11 +627,9 @@ static void LoadProperties(char* data, const char* filter, const char* filename,
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
- static constexpr const char* const kVendorPathPrefixes[4] = {
+ static constexpr const char* const kVendorPathPrefixes[2] = {
"/vendor",
"/odm",
- "/vendor_dlkm",
- "/odm_dlkm",
};
const char* context = kInitContext;
@@ -723,8 +708,8 @@ static void LoadProperties(char* data, const char* filter, const char* filename,
if (it == properties->end()) {
(*properties)[key] = value;
} else if (it->second != value) {
- LOG(WARNING) << "Overriding previous property '" << key << "':'" << it->second
- << "' with new value '" << value << "'";
+ LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
+ << it->second << "' with new value '" << value << "'";
it->second = value;
}
} else {
@@ -753,15 +738,6 @@ static bool load_properties_from_file(const char* filename, const char* filter,
return true;
}
-static void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {
- std::string prop = GetRamdiskPropForSecondStage();
- if (access(prop.c_str(), R_OK) != 0) {
- CHECK(errno == ENOENT) << "Cannot access " << prop << ": " << strerror(errno);
- return;
- }
- load_properties_from_file(prop.c_str(), nullptr, properties);
-}
-
// persist.sys.usb.config values can't be combined on build-time when property
// files are split into each partition.
// So we need to apply the same rule of build/make/tools/post_process_props.py
@@ -863,44 +839,23 @@ static void property_initialize_ro_product_props() {
}
}
-static void property_initialize_build_id() {
- std::string build_id = GetProperty(ID_PROP, "");
- if (!build_id.empty()) {
+// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
+static void property_derive_build_fingerprint() {
+ std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
+ if (!build_fingerprint.empty()) {
return;
}
- std::string legacy_build_id = GetProperty(LEGACY_ID_PROP, "");
- std::string vbmeta_digest = GetProperty(VBMETA_DIGEST_PROP, "");
- if (vbmeta_digest.size() < DIGEST_SIZE_USED) {
- LOG(ERROR) << "vbmeta digest size too small " << vbmeta_digest;
- // Still try to set the id field in the unexpected case.
- build_id = legacy_build_id;
- } else {
- // Derive the ro.build.id by appending the vbmeta digest to the base value.
- build_id = legacy_build_id + "." + vbmeta_digest.substr(0, DIGEST_SIZE_USED);
- }
-
- std::string error;
- auto res = PropertySet(ID_PROP, build_id, &error);
- if (res != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id;
- }
-}
-
-static std::string ConstructBuildFingerprint(bool legacy) {
const std::string UNKNOWN = "unknown";
- std::string build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
+ build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.product.name", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.product.device", UNKNOWN);
build_fingerprint += ':';
- build_fingerprint += GetProperty("ro.build.version.release_or_codename", UNKNOWN);
+ build_fingerprint += GetProperty("ro.build.version.release", UNKNOWN);
build_fingerprint += '/';
-
- std::string build_id =
- legacy ? GetProperty(LEGACY_ID_PROP, UNKNOWN) : GetProperty(ID_PROP, UNKNOWN);
- build_fingerprint += build_id;
+ build_fingerprint += GetProperty("ro.build.id", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.version.incremental", UNKNOWN);
build_fingerprint += ':';
@@ -908,181 +863,41 @@ static std::string ConstructBuildFingerprint(bool legacy) {
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.tags", UNKNOWN);
- return build_fingerprint;
-}
-
-// Derive the legacy build fingerprint if we overwrite the build id at runtime.
-static void property_derive_legacy_build_fingerprint() {
- std::string legacy_build_fingerprint = GetProperty(LEGACY_FINGERPRINT_PROP, "");
- if (!legacy_build_fingerprint.empty()) {
- return;
- }
-
- // The device doesn't have a legacy build id, skipping the legacy fingerprint.
- std::string legacy_build_id = GetProperty(LEGACY_ID_PROP, "");
- if (legacy_build_id.empty()) {
- return;
- }
-
- legacy_build_fingerprint = ConstructBuildFingerprint(true /* legacy fingerprint */);
- LOG(INFO) << "Setting property '" << LEGACY_FINGERPRINT_PROP << "' to '"
- << legacy_build_fingerprint << "'";
-
- std::string error;
- uint32_t res = PropertySet(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error);
- if (res != PROP_SUCCESS) {
- LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res
- << " (" << error << ")";
- }
-}
-
-// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
-static void property_derive_build_fingerprint() {
- std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
- if (!build_fingerprint.empty()) {
- return;
- }
-
- build_fingerprint = ConstructBuildFingerprint(false /* legacy fingerprint */);
- LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'";
+ LOG(INFO) << "Setting property 'ro.build.fingerprint' to '" << build_fingerprint << "'";
std::string error;
- uint32_t res = PropertySet(FINGERPRINT_PROP, build_fingerprint, &error);
+ uint32_t res = PropertySet("ro.build.fingerprint", build_fingerprint, &error);
if (res != PROP_SUCCESS) {
- LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " ("
- << error << ")";
- }
-}
-
-// If the ro.product.cpu.abilist* properties have not been explicitly
-// set, derive them from ro.${partition}.product.cpu.abilist* properties.
-static void property_initialize_ro_cpu_abilist() {
- // From high to low priority.
- const char* kAbilistSources[] = {
- "product",
- "odm",
- "vendor",
- "system",
- };
- const std::string EMPTY = "";
- const char* kAbilistProp = "ro.product.cpu.abilist";
- const char* kAbilist32Prop = "ro.product.cpu.abilist32";
- const char* kAbilist64Prop = "ro.product.cpu.abilist64";
-
- // If the properties are defined explicitly, just use them.
- if (GetProperty(kAbilistProp, EMPTY) != EMPTY) {
- return;
- }
-
- // Find the first source defining these properties by order.
- std::string abilist32_prop_val;
- std::string abilist64_prop_val;
- for (const auto& source : kAbilistSources) {
- const auto abilist32_prop = std::string("ro.") + source + ".product.cpu.abilist32";
- const auto abilist64_prop = std::string("ro.") + source + ".product.cpu.abilist64";
- abilist32_prop_val = GetProperty(abilist32_prop, EMPTY);
- abilist64_prop_val = GetProperty(abilist64_prop, EMPTY);
- // The properties could be empty on 32-bit-only or 64-bit-only devices,
- // but we cannot identify a property is empty or undefined by GetProperty().
- // So, we assume both of these 2 properties are empty as undefined.
- if (abilist32_prop_val != EMPTY || abilist64_prop_val != EMPTY) {
- break;
- }
- }
-
- // Merge ABI lists for ro.product.cpu.abilist
- auto abilist_prop_val = abilist64_prop_val;
- if (abilist32_prop_val != EMPTY) {
- if (abilist_prop_val != EMPTY) {
- abilist_prop_val += ",";
- }
- abilist_prop_val += abilist32_prop_val;
- }
-
- // Set these properties
- const std::pair<const char*, const std::string&> set_prop_list[] = {
- {kAbilistProp, abilist_prop_val},
- {kAbilist32Prop, abilist32_prop_val},
- {kAbilist64Prop, abilist64_prop_val},
- };
- for (const auto& [prop, prop_val] : set_prop_list) {
- LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'";
-
- std::string error;
- uint32_t res = PropertySet(prop, prop_val, &error);
- if (res != PROP_SUCCESS) {
- LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error
- << ")";
- }
+ LOG(ERROR) << "Error setting property 'ro.build.fingerprint': err=" << res << " (" << error
+ << ")";
}
}
void PropertyLoadBootDefaults() {
+ // TODO(b/117892318): merge prop.default and build.prop files into one
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
// property files, regardless of if they are "ro." properties or not.
std::map<std::string, std::string> properties;
-
- if (IsRecoveryMode()) {
- load_properties_from_file("/prop.default", nullptr, &properties);
- }
-
- // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
- // Falling back to /<part>/defalt.prop and /<part>/build.prop only when legacy path has to
- // be supported, which is controlled by the support_legacy_path_until argument.
- const auto load_properties_from_partition = [&properties](const std::string& partition,
- int support_legacy_path_until) {
- auto path = "/" + partition + "/etc/build.prop";
- if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
- return;
+ if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
+ // Try recovery path
+ if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
+ // Try legacy path
+ load_properties_from_file("/default.prop", nullptr, &properties);
}
- // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
- // separate map. Then by comparing its value with legacy_version, we know that if the
- // partition is old enough so that we need to respect the legacy paths.
- std::map<std::string, std::string> temp;
- auto legacy_path1 = "/" + partition + "/default.prop";
- auto legacy_path2 = "/" + partition + "/build.prop";
- load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
- load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
- bool support_legacy_path = false;
- auto version_prop_name = "ro." + partition + ".build.version.sdk";
- auto it = temp.find(version_prop_name);
- if (it == temp.end()) {
- // This is embarassing. Without the prop, we can't determine how old the partition is.
- // Let's be conservative by assuming it is very very old.
- support_legacy_path = true;
- } else if (int value;
- ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
- support_legacy_path = true;
- }
- if (support_legacy_path) {
- // We don't update temp into properties directly as it might skip any (future) logic
- // for resolving duplicates implemented in load_properties_from_file. Instead, read
- // the files again into the properties map.
- load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
- load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
- } else {
- LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
- << "because " << version_prop_name << "(" << it->second << ") is newer "
- << "than " << support_legacy_path_until;
- }
- };
-
- // Order matters here. The more the partition is specific to a product, the higher its
- // precedence is.
- LoadPropertiesFromSecondStageRes(&properties);
+ }
load_properties_from_file("/system/build.prop", nullptr, &properties);
- load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
- // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
- // all updated.
- // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+ load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
load_properties_from_file("/vendor/default.prop", nullptr, &properties);
- // }
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
- load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
- load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
- load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
- load_properties_from_partition("product", /* support_legacy_path_until */ 30);
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
+ load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
+ } else {
+ load_properties_from_file("/odm/default.prop", nullptr, &properties);
+ load_properties_from_file("/odm/build.prop", nullptr, &properties);
+ }
+ load_properties_from_file("/product/build.prop", nullptr, &properties);
+ load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
if (access(kDebugRamdiskProp, R_OK) == 0) {
LOG(INFO) << "Loading " << kDebugRamdiskProp;
@@ -1098,10 +913,7 @@ void PropertyLoadBootDefaults() {
}
property_initialize_ro_product_props();
- property_initialize_build_id();
property_derive_build_fingerprint();
- property_derive_legacy_build_fingerprint();
- property_initialize_ro_cpu_abilist();
update_sys_usb_config();
}
@@ -1133,7 +945,7 @@ void CreateSerializedPropertyInfo() {
&property_infos)) {
return;
}
- // Don't check for failure here, since we don't always have all of these partitions.
+ // Don't check for failure here, so we always have a sane list of properties.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
@@ -1228,23 +1040,22 @@ static void ProcessKernelDt() {
}
}
-constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
-
static void ProcessKernelCmdline() {
+ bool for_emulator = false;
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
- if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
- InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
+ if (key == "qemu") {
+ for_emulator = true;
+ } else if (StartsWith(key, "androidboot.")) {
+ InitPropertySet("ro.boot." + key.substr(12), value);
}
});
-}
-
-static void ProcessBootconfig() {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
- InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
- }
- });
+ if (for_emulator) {
+ ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+ // In the emulator, export any kernel option with the "ro.kernel." prefix.
+ InitPropertySet("ro.kernel." + key, value);
+ });
+ }
}
void PropertyInit() {
@@ -1265,7 +1076,6 @@ void PropertyInit() {
// properties set in DT always have priority over the command-line ones.
ProcessKernelDt();
ProcessKernelCmdline();
- ProcessBootconfig();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index ac6b7b271..c6dcfa257 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -23,7 +23,6 @@
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
-#include <android-base/strings.h>
#include <gtest/gtest.h>
using android::base::GetProperty;
@@ -91,39 +90,5 @@ TEST(property_service, userspace_reboot_not_supported) {
EXPECT_FALSE(SetProperty("sys.powerctl", "reboot,userspace"));
}
-TEST(property_service, check_fingerprint_with_legacy_build_id) {
- std::string legacy_build_id = GetProperty("ro.build.legacy.id", "");
- if (legacy_build_id.empty()) {
- GTEST_SKIP() << "Skipping test, legacy build id isn't set.";
- }
-
- std::string vbmeta_digest = GetProperty("ro.boot.vbmeta.digest", "");
- ASSERT_GE(vbmeta_digest.size(), 8u);
- std::string build_id = GetProperty("ro.boot.build.id", "");
- // Check that the build id is constructed with the prefix of vbmeta digest
- std::string expected_build_id = legacy_build_id + "." + vbmeta_digest.substr(0, 8);
- ASSERT_EQ(expected_build_id, build_id);
- // Check that the fingerprint is constructed with the expected format.
- std::string fingerprint = GetProperty("ro.build.fingerprint", "");
- std::vector<std::string> fingerprint_fields = {
- GetProperty("ro.product.brand", ""),
- "/",
- GetProperty("ro.product.name", ""),
- "/",
- GetProperty("ro.product.device", ""),
- ":",
- GetProperty("ro.build.version.release_or_codename", ""),
- "/",
- expected_build_id,
- "/",
- GetProperty("ro.build.version.incremental", ""),
- ":",
- GetProperty("ro.build.type", ""),
- "/",
- GetProperty("ro.build.tags", "")};
-
- ASSERT_EQ(android::base::Join(fingerprint_fields, ""), fingerprint);
-}
-
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a0ae4b4d4..a4fb08e0b 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -450,22 +450,10 @@ static UmountStat TryUmountAndFsck(unsigned int cmd, bool run_fsck,
// zram is able to use backing device on top of a loopback device.
// In order to unmount /data successfully, we have to kill the loopback device first
-#define ZRAM_DEVICE "/dev/block/zram0"
-#define ZRAM_RESET "/sys/block/zram0/reset"
-#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
-#define ZRAM_INITSTATE "/sys/block/zram0/initstate"
+#define ZRAM_DEVICE "/dev/block/zram0"
+#define ZRAM_RESET "/sys/block/zram0/reset"
+#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
static Result<void> KillZramBackingDevice() {
- std::string zram_initstate;
- if (!android::base::ReadFileToString(ZRAM_INITSTATE, &zram_initstate)) {
- return ErrnoError() << "Failed to read " << ZRAM_INITSTATE;
- }
-
- zram_initstate.erase(zram_initstate.length() - 1);
- if (zram_initstate == "0") {
- LOG(INFO) << "Zram has not been swapped on";
- return {};
- }
-
if (access(ZRAM_BACK_DEV, F_OK) != 0 && errno == ENOENT) {
LOG(INFO) << "No zram backing device configured";
return {};
@@ -545,8 +533,8 @@ static void StopServices(const std::set<std::string>& services, std::chrono::mil
// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
// Returns number of violators.
-int StopServicesAndLogViolations(const std::set<std::string>& services,
- std::chrono::milliseconds timeout, bool terminate) {
+static int StopServicesAndLogViolations(const std::set<std::string>& services,
+ std::chrono::milliseconds timeout, bool terminate) {
StopServices(services, timeout, terminate);
int still_running = 0;
for (const auto& s : ServiceList::GetInstance()) {
@@ -560,18 +548,6 @@ int StopServicesAndLogViolations(const std::set<std::string>& services,
return still_running;
}
-static Result<void> UnmountAllApexes() {
- const char* args[] = {"/system/bin/apexd", "--unmount-all"};
- int status;
- if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
- return ErrnoError() << "Failed to call '/system/bin/apexd --unmount-all'";
- }
- if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
- return {};
- }
- return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
-}
-
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
@@ -670,7 +646,6 @@ static void DoReboot(unsigned int cmd, const std::string& reason, const std::str
if (do_shutdown_animation) {
SetProperty("service.bootanim.exit", "0");
- SetProperty("service.bootanim.progress", "0");
// Could be in the middle of animation. Stop and start so that it can pick
// up the right mode.
boot_anim->Stop();
@@ -729,11 +704,6 @@ static void DoReboot(unsigned int cmd, const std::string& reason, const std::str
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
- LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
- // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
- if (auto ret = UnmountAllApexes(); !ret.ok()) {
- LOG(ERROR) << ret.error();
- }
UmountStat stat =
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
@@ -772,6 +742,18 @@ static void LeaveShutdown() {
StartSendingMessages();
}
+static Result<void> UnmountAllApexes() {
+ const char* args[] = {"/system/bin/apexd", "--unmount-all"};
+ int status;
+ if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
+ return ErrnoError() << "Failed to call '/system/bin/apexd --unmount-all'";
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return {};
+ }
+ return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
+}
+
static std::chrono::milliseconds GetMillisProperty(const std::string& name,
std::chrono::milliseconds default_value) {
auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
@@ -823,19 +805,11 @@ static Result<void> DoUserspaceReboot() {
auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
<< "Timeout to kill services: " << sigkill_timeout.count() << "ms";
- std::string services_file_name = "/metadata/userspacereboot/services.txt";
- const int flags = O_RDWR | O_CREAT | O_SYNC | O_APPEND | O_CLOEXEC;
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
r > 0) {
- auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
- android::base::WriteStringToFd("Post-data services still running: \n", fd);
- for (const auto& s : ServiceList::GetInstance()) {
- if (s->IsRunning() && stop_first.count(s->name())) {
- android::base::WriteStringToFd(s->name() + "\n", fd);
- }
- }
sub_reason = "sigkill";
+ // TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " post-data services are still running";
}
if (auto result = KillZramBackingDevice(); !result.ok()) {
@@ -850,14 +824,8 @@ static Result<void> DoUserspaceReboot() {
if (int r = StopServicesAndLogViolations(debugging_services, sigkill_timeout,
false /* SIGKILL */);
r > 0) {
- auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
- android::base::WriteStringToFd("Debugging services still running: \n", fd);
- for (const auto& s : ServiceList::GetInstance()) {
- if (s->IsRunning() && debugging_services.count(s->name())) {
- android::base::WriteStringToFd(s->name() + "\n", fd);
- }
- }
sub_reason = "sigkill_debug";
+ // TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
{
@@ -870,7 +838,7 @@ static Result<void> DoUserspaceReboot() {
sub_reason = "apex";
return result;
}
- if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP).ok()) {
+ if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
diff --git a/init/reboot.h b/init/reboot.h
index 551a114f6..81c3edc12 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -17,17 +17,11 @@
#ifndef _INIT_REBOOT_H
#define _INIT_REBOOT_H
-#include <chrono>
-#include <set>
#include <string>
namespace android {
namespace init {
-// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
-// Returns number of violators.
-int StopServicesAndLogViolations(const std::set<std::string>& services,
- std::chrono::milliseconds timeout, bool terminate);
// Parses and handles a setprop sys.powerctl message.
void HandlePowerctlMessage(const std::string& command);
diff --git a/init/reboot_test.cpp b/init/reboot_test.cpp
deleted file mode 100644
index b3d038d14..000000000
--- a/init/reboot_test.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "reboot.h"
-
-#include <errno.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <gtest/gtest.h>
-#include <selinux/selinux.h>
-
-#include "builtin_arguments.h"
-#include "builtins.h"
-#include "parser.h"
-#include "service_list.h"
-#include "service_parser.h"
-#include "subcontext.h"
-#include "util.h"
-
-using namespace std::literals;
-
-using android::base::GetProperty;
-using android::base::Join;
-using android::base::SetProperty;
-using android::base::Split;
-using android::base::StringReplace;
-using android::base::WaitForProperty;
-using android::base::WriteStringToFd;
-
-namespace android {
-namespace init {
-
-class RebootTest : public ::testing::Test {
- public:
- RebootTest() {
- std::vector<std::string> names = GetServiceNames();
- if (!names.empty()) {
- ADD_FAILURE() << "Expected empty ServiceList but found: [" << Join(names, ',') << "]";
- }
- }
-
- ~RebootTest() {
- std::vector<std::string> names = GetServiceNames();
- for (const auto& name : names) {
- auto s = ServiceList::GetInstance().FindService(name);
- auto pid = s->pid();
- ServiceList::GetInstance().RemoveService(*s);
- if (pid > 0) {
- kill(pid, SIGTERM);
- kill(pid, SIGKILL);
- }
- }
- }
-
- private:
- std::vector<std::string> GetServiceNames() const {
- std::vector<std::string> names;
- for (const auto& s : ServiceList::GetInstance()) {
- names.push_back(s->name());
- }
- return names;
- }
-};
-
-std::string GetSecurityContext() {
- char* ctx;
- if (getcon(&ctx) == -1) {
- ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
- }
- std::string result = std::string(ctx);
- freecon(ctx);
- return result;
-}
-
-void AddTestService(const std::string& name) {
- static constexpr std::string_view kScriptTemplate = R"init(
-service $name /system/bin/yes
- user shell
- group shell
- seclabel $selabel
-)init";
-
- std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", name, false),
- "$selabel", GetSecurityContext(), false);
- ServiceList& service_list = ServiceList::GetInstance();
- Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
-
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFd(script, tf.fd));
- ASSERT_TRUE(parser.ParseConfig(tf.path));
-}
-
-TEST_F(RebootTest, StopServicesSIGTERM) {
- if (getuid() != 0) {
- GTEST_SKIP() << "Skipping test, must be run as root.";
- return;
- }
-
- AddTestService("A");
- AddTestService("B");
-
- auto service_a = ServiceList::GetInstance().FindService("A");
- ASSERT_NE(nullptr, service_a);
- auto service_b = ServiceList::GetInstance().FindService("B");
- ASSERT_NE(nullptr, service_b);
-
- ASSERT_RESULT_OK(service_a->Start());
- ASSERT_TRUE(service_a->IsRunning());
- ASSERT_RESULT_OK(service_b->Start());
- ASSERT_TRUE(service_b->IsRunning());
-
- std::unique_ptr<Service> oneshot_service;
- {
- auto result = Service::MakeTemporaryOneshotService(
- {"exec", GetSecurityContext(), "--", "/system/bin/yes"});
- ASSERT_RESULT_OK(result);
- oneshot_service = std::move(*result);
- }
- std::string oneshot_service_name = oneshot_service->name();
- oneshot_service->Start();
- ASSERT_TRUE(oneshot_service->IsRunning());
- ServiceList::GetInstance().AddService(std::move(oneshot_service));
-
- EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
- /* terminate= */ true));
- EXPECT_FALSE(service_a->IsRunning());
- EXPECT_FALSE(service_b->IsRunning());
- // Oneshot services are deleted from the ServiceList after they are destroyed.
- auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
- EXPECT_EQ(nullptr, oneshot_service_after_stop);
-}
-
-TEST_F(RebootTest, StopServicesSIGKILL) {
- if (getuid() != 0) {
- GTEST_SKIP() << "Skipping test, must be run as root.";
- return;
- }
-
- AddTestService("A");
- AddTestService("B");
-
- auto service_a = ServiceList::GetInstance().FindService("A");
- ASSERT_NE(nullptr, service_a);
- auto service_b = ServiceList::GetInstance().FindService("B");
- ASSERT_NE(nullptr, service_b);
-
- ASSERT_RESULT_OK(service_a->Start());
- ASSERT_TRUE(service_a->IsRunning());
- ASSERT_RESULT_OK(service_b->Start());
- ASSERT_TRUE(service_b->IsRunning());
-
- std::unique_ptr<Service> oneshot_service;
- {
- auto result = Service::MakeTemporaryOneshotService(
- {"exec", GetSecurityContext(), "--", "/system/bin/yes"});
- ASSERT_RESULT_OK(result);
- oneshot_service = std::move(*result);
- }
- std::string oneshot_service_name = oneshot_service->name();
- oneshot_service->Start();
- ASSERT_TRUE(oneshot_service->IsRunning());
- ServiceList::GetInstance().AddService(std::move(oneshot_service));
-
- EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
- /* terminate= */ false));
- EXPECT_FALSE(service_a->IsRunning());
- EXPECT_FALSE(service_b->IsRunning());
- // Oneshot services are deleted from the ServiceList after they are destroyed.
- auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
- EXPECT_EQ(nullptr, oneshot_service_after_stop);
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index b3fa9fd3b..76460a5df 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -19,7 +19,6 @@
#include <sys/syscall.h>
#include <unistd.h>
-#include <optional>
#include <string>
#include <android-base/file.h>
@@ -31,7 +30,6 @@
#include "capabilities.h"
#include "reboot_utils.h"
-#include "util.h"
namespace android {
namespace init {
@@ -39,51 +37,26 @@ namespace init {
static std::string init_fatal_reboot_target = "bootloader";
static bool init_fatal_panic = false;
-// this needs to read the /proc/* files directly because it is called before
-// ro.boot.* properties are initialized
-void SetFatalRebootTarget(const std::optional<std::string>& reboot_target) {
+void SetFatalRebootTarget() {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
cmdline = android::base::Trim(cmdline);
- const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";
- if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {
- init_fatal_panic = false;
- ImportBootconfig(
- [kInitFatalPanicParamString](const std::string& key, const std::string& value) {
- if (key == kInitFatalPanicParamString && value == "true") {
- init_fatal_panic = true;
- }
- });
- } else {
- const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";
- init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
- }
-
- if (reboot_target) {
- init_fatal_reboot_target = *reboot_target;
- return;
- }
+ const char kInitFatalPanicString[] = "androidboot.init_fatal_panic=true";
+ init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
- const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";
+ const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
auto start_pos = cmdline.find(kRebootTargetString);
if (start_pos == std::string::npos) {
- ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {
- if (key == kRebootTargetString) {
- init_fatal_reboot_target = value;
- }
- });
- // We already default to bootloader if no setting is provided.
- } else {
- const std::string kRebootTargetStringPattern = kRebootTargetString + "=";
- start_pos += sizeof(kRebootTargetStringPattern) - 1;
-
- auto end_pos = cmdline.find(' ', start_pos);
- // if end_pos isn't found, then we've run off the end, but this is okay as this is the last
- // entry, and -1 is a valid size for string::substr();
- auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;
- init_fatal_reboot_target = cmdline.substr(start_pos, size);
+ return; // We already default to bootloader if no setting is provided.
}
+ start_pos += sizeof(kRebootTargetString) - 1;
+
+ auto end_pos = cmdline.find(' ', start_pos);
+ // if end_pos isn't found, then we've run off the end, but this is okay as this is the last
+ // entry, and -1 is a valid size for string::substr();
+ auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;
+ init_fatal_reboot_target = cmdline.substr(start_pos, size);
}
bool IsRebootCapable() {
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index a0023b9bb..05bb9ae8e 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -16,7 +16,6 @@
#pragma once
-#include <optional>
#include <string>
#define PROC_SYSRQ "/proc/sysrq-trigger"
@@ -24,7 +23,7 @@
namespace android {
namespace init {
-void SetFatalRebootTarget(const std::optional<std::string>& reboot_target = std::nullopt);
+void SetFatalRebootTarget();
// Determines whether the system is capable of rebooting. This is conservative,
// so if any of the attempts to determine this fail, it will still return true.
bool IsRebootCapable();
diff --git a/init/second_stage_resources.h b/init/second_stage_resources.h
deleted file mode 100644
index 544d16f53..000000000
--- a/init/second_stage_resources.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace init {
-
-constexpr const char kSecondStageRes[] = "/second_stage_resources";
-constexpr const char kBootImageRamdiskProp[] = "/system/etc/ramdisk/build.prop";
-
-inline std::string GetRamdiskPropForSecondStage() {
- return std::string(kSecondStageRes) + kBootImageRamdiskProp;
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/security.cpp b/init/security.cpp
index 970696e64..6cbe642c0 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -19,7 +19,6 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/perf_event.h>
-#include <selinux/selinux.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>
@@ -36,6 +35,59 @@ using android::base::SetProperty;
namespace android {
namespace init {
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
+ unique_fd hwrandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (hwrandom_fd == -1) {
+ if (errno == ENOENT) {
+ LOG(INFO) << "/dev/hw_random not found";
+ // It's not an error to not have a Hardware RNG.
+ return {};
+ }
+ return ErrnoError() << "Failed to open /dev/hw_random";
+ }
+
+ unique_fd urandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (urandom_fd == -1) {
+ return ErrnoError() << "Failed to open /dev/urandom";
+ }
+
+ char buf[512];
+ size_t total_bytes_written = 0;
+ while (total_bytes_written < sizeof(buf)) {
+ ssize_t chunk_size =
+ TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to read from /dev/hw_random";
+ } else if (chunk_size == 0) {
+ return Error() << "Failed to read from /dev/hw_random: EOF";
+ }
+
+ chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to write to /dev/urandom";
+ }
+ total_bytes_written += chunk_size;
+ }
+
+ LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+ return {};
+}
+
static bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {
std::ifstream inf(path, std::fstream::in);
if (!inf) {
@@ -76,7 +128,8 @@ static bool SetHighestAvailableOptionValue(const std::string& path, int min, int
#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-static bool SetMmapRndBitsMin(int start, int min, bool compat) {
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
std::string path;
if (compat) {
path = MMAP_RND_COMPAT_PATH;
@@ -121,6 +174,9 @@ Result<void> SetMmapRndBitsAction(const BuiltinArguments&) {
if (SetMmapRndBitsMin(16, 16, h64)) {
return {};
}
+#elif defined(__mips__) || defined(__mips64__)
+ // TODO: add mips support b/27788820
+ return {};
#else
LOG(ERROR) << "Unknown architecture";
#endif
@@ -170,19 +226,6 @@ Result<void> SetKptrRestrictAction(const BuiltinArguments&) {
// supporting kernels that precede the perf_event_open hooks (Android common
// kernels 4.4 and 4.9).
Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {
- // Special case: for *development devices* that boot with permissive
- // SELinux, treat the LSM hooks as present for the effect of lowering the
- // perf_event_paranoid sysctl. The sysprop is reused for pragmatic reasons,
- // as there no existing way for init rules to check for permissive boot at
- // the time of writing.
- if (ALLOW_PERMISSIVE_SELINUX) {
- if (!security_getenforce()) {
- LOG(INFO) << "Permissive SELinux boot, forcing sys.init.perf_lsm_hooks to 1.";
- SetProperty("sys.init.perf_lsm_hooks", "1");
- return {};
- }
- }
-
// Use a trivial event that will be configured, but not started.
struct perf_event_attr pe = {
.type = PERF_TYPE_SOFTWARE,
diff --git a/init/security.h b/init/security.h
index e8bec6a51..43c273969 100644
--- a/init/security.h
+++ b/init/security.h
@@ -26,6 +26,7 @@
namespace android {
namespace init {
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
Result<void> SetKptrRestrictAction(const BuiltinArguments&);
Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&);
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 42d302324..5a0255acd 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -45,7 +45,7 @@
// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
// have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
// compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the
-// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+// LoadSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
// That function contains even more documentation with the specific implementation details of how
// the SEPolicy is compiled if needed.
@@ -63,7 +63,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include <android-base/result.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
@@ -75,7 +74,6 @@
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
#include "reboot_utils.h"
-#include "snapuserd_transition.h"
#include "util.h"
using namespace std::string_literals;
@@ -93,7 +91,7 @@ namespace {
enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-EnforcingStatus StatusFromProperty() {
+EnforcingStatus StatusFromCmdline() {
EnforcingStatus status = SELINUX_ENFORCING;
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
@@ -102,20 +100,12 @@ EnforcingStatus StatusFromProperty() {
}
});
- if (status == SELINUX_ENFORCING) {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
- }
- });
- }
-
return status;
}
bool IsEnforcing() {
if (ALLOW_PERMISSIVE_SELINUX) {
- return StatusFromProperty() == SELINUX_ENFORCING;
+ return StatusFromCmdline() == SELINUX_ENFORCING;
}
return true;
}
@@ -223,8 +213,8 @@ bool ReadFirstLine(const char* file, std::string* line) {
return true;
}
-Result<std::string> FindPrecompiledSplitPolicy() {
- std::string precompiled_sepolicy;
+bool FindPrecompiledSplitPolicy(std::string* file) {
+ file->clear();
// If there is an odm partition, precompiled_sepolicy will be in
// odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
static constexpr const char vendor_precompiled_sepolicy[] =
@@ -232,49 +222,62 @@ Result<std::string> FindPrecompiledSplitPolicy() {
static constexpr const char odm_precompiled_sepolicy[] =
"/odm/etc/selinux/precompiled_sepolicy";
if (access(odm_precompiled_sepolicy, R_OK) == 0) {
- precompiled_sepolicy = odm_precompiled_sepolicy;
+ *file = odm_precompiled_sepolicy;
} else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
- precompiled_sepolicy = vendor_precompiled_sepolicy;
+ *file = vendor_precompiled_sepolicy;
} else {
- return ErrnoError() << "No precompiled sepolicy at " << vendor_precompiled_sepolicy;
+ PLOG(INFO) << "No precompiled sepolicy";
+ return false;
}
-
- // Use precompiled sepolicy only when all corresponding hashes are equal.
- std::vector<std::pair<std::string, std::string>> sepolicy_hashes{
- {"/system/etc/selinux/plat_sepolicy_and_mapping.sha256",
- precompiled_sepolicy + ".plat_sepolicy_and_mapping.sha256"},
- {"/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256",
- precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
- {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
- precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
- };
-
- for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
- // Both of them should exist or both of them shouldn't exist.
- if (access(actual_id_path.c_str(), R_OK) != 0) {
- if (access(precompiled_id_path.c_str(), R_OK) == 0) {
- return Error() << precompiled_id_path << " exists but " << actual_id_path
- << " doesn't";
- }
- continue;
- }
-
- std::string actual_id;
- if (!ReadFirstLine(actual_id_path.c_str(), &actual_id)) {
- return ErrnoError() << "Failed to read " << actual_id_path;
- }
-
- std::string precompiled_id;
- if (!ReadFirstLine(precompiled_id_path.c_str(), &precompiled_id)) {
- return ErrnoError() << "Failed to read " << precompiled_id_path;
- }
-
- if (actual_id.empty() || actual_id != precompiled_id) {
- return Error() << actual_id_path << " and " << precompiled_id_path << " differ";
- }
+ std::string actual_plat_id;
+ if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
+ return false;
+ }
+ std::string actual_system_ext_id;
+ if (!ReadFirstLine("/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256",
+ &actual_system_ext_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256";
+ return false;
+ }
+ std::string actual_product_id;
+ if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
+ &actual_product_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/product/etc/selinux/product_sepolicy_and_mapping.sha256";
+ return false;
}
- return precompiled_sepolicy;
+ std::string precompiled_plat_id;
+ std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_plat_sha256;
+ file->clear();
+ return false;
+ }
+ std::string precompiled_system_ext_id;
+ std::string precompiled_system_ext_sha256 = *file + ".system_ext_sepolicy_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_system_ext_sha256.c_str(), &precompiled_system_ext_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_system_ext_sha256;
+ file->clear();
+ return false;
+ }
+ std::string precompiled_product_id;
+ std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_product_sha256;
+ file->clear();
+ return false;
+ }
+ if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
+ actual_system_ext_id.empty() || actual_system_ext_id != precompiled_system_ext_id ||
+ actual_product_id.empty() || actual_product_id != precompiled_product_id) {
+ file->clear();
+ return false;
+ }
+ return true;
}
bool GetVendorMappingVersion(std::string* plat_vers) {
@@ -295,12 +298,7 @@ bool IsSplitPolicyDevice() {
return access(plat_policy_cil_file, R_OK) != -1;
}
-struct PolicyFile {
- unique_fd fd;
- std::string path;
-};
-
-bool OpenSplitPolicy(PolicyFile* policy_file) {
+bool LoadSplitPolicy() {
// IMPLEMENTATION NOTE: Split policy consists of three CIL files:
// * platform -- policy needed due to logic contained in the system image,
// * non-platform -- policy needed due to logic contained in the vendor image,
@@ -321,18 +319,17 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
// must match the platform policy on the system image.
+ std::string precompiled_sepolicy_file;
// use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
// Thus it cannot use the precompiled policy from vendor image.
- if (!use_userdebug_policy) {
- if (auto res = FindPrecompiledSplitPolicy(); res.ok()) {
- unique_fd fd(open(res->c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- if (fd != -1) {
- policy_file->fd = std::move(fd);
- policy_file->path = std::move(*res);
- return true;
+ if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+ unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+ if (fd != -1) {
+ if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+ return false;
}
- } else {
- LOG(INFO) << res.error();
+ return true;
}
}
// No suitable precompiled policy could be loaded
@@ -372,12 +369,6 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
system_ext_mapping_file.clear();
}
- std::string system_ext_compat_cil_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +
- ".compat.cil");
- if (access(system_ext_compat_cil_file.c_str(), F_OK) == -1) {
- system_ext_compat_cil_file.clear();
- }
-
std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
product_policy_cil_file.clear();
@@ -432,9 +423,6 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
if (!system_ext_mapping_file.empty()) {
compile_args.push_back(system_ext_mapping_file.c_str());
}
- if (!system_ext_compat_cil_file.empty()) {
- compile_args.push_back(system_ext_compat_cil_file.c_str());
- }
if (!product_policy_cil_file.empty()) {
compile_args.push_back(product_policy_cil_file.c_str());
}
@@ -458,39 +446,34 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
}
unlink(compiled_sepolicy);
- policy_file->fd = std::move(compiled_sepolicy_fd);
- policy_file->path = compiled_sepolicy;
+ LOG(INFO) << "Loading compiled SELinux policy";
+ if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+ return false;
+ }
+
return true;
}
-bool OpenMonolithicPolicy(PolicyFile* policy_file) {
- static constexpr char kSepolicyFile[] = "/sepolicy";
-
- LOG(VERBOSE) << "Opening SELinux policy from monolithic file";
- policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
- if (policy_file->fd < 0) {
- PLOG(ERROR) << "Failed to open monolithic SELinux policy";
+bool LoadMonolithicPolicy() {
+ LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+ if (selinux_android_load_policy() < 0) {
+ PLOG(ERROR) << "Failed to load monolithic SELinux policy";
return false;
}
- policy_file->path = kSepolicyFile;
return true;
}
-void ReadPolicy(std::string* policy) {
- PolicyFile policy_file;
+bool LoadPolicy() {
+ return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
- bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)
- : OpenMonolithicPolicy(&policy_file);
- if (!ok) {
- LOG(FATAL) << "Unable to open SELinux policy";
+void SelinuxInitialize() {
+ LOG(INFO) << "Loading SELinux policy";
+ if (!LoadPolicy()) {
+ LOG(FATAL) << "Unable to load SELinux policy";
}
- if (!android::base::ReadFdToString(policy_file.fd, policy)) {
- PLOG(FATAL) << "Failed to read policy file: " << policy_file.path;
- }
-}
-
-void SelinuxSetEnforcement() {
bool kernel_enforcing = (security_getenforce() == 1);
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
@@ -551,7 +534,6 @@ void SelinuxRestoreContext() {
selinux_android_restorecon("/dev/__properties__", 0);
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
@@ -667,7 +649,7 @@ void MountMissingSystemPartitions() {
extra_fstab.emplace_back(std::move(entry));
}
- SkipMountingPartitions(&extra_fstab, true /* verbose */);
+ SkipMountingPartitions(&extra_fstab);
if (extra_fstab.empty()) {
return;
}
@@ -687,30 +669,6 @@ void MountMissingSystemPartitions() {
}
}
-static void LoadSelinuxPolicy(std::string& policy) {
- LOG(INFO) << "Loading SELinux policy";
-
- set_selinuxmnt("/sys/fs/selinux");
- if (security_load_policy(policy.data(), policy.size()) < 0) {
- PLOG(FATAL) << "SELinux: Could not load policy";
- }
-}
-
-// The SELinux setup process is carefully orchestrated around snapuserd. Policy
-// must be loaded off dynamic partitions, and during an OTA, those partitions
-// cannot be read without snapuserd. But, with kernel-privileged snapuserd
-// running, loading the policy will immediately trigger audits.
-//
-// We use a five-step process to address this:
-// (1) Read the policy into a string, with snapuserd running.
-// (2) Rewrite the snapshot device-mapper tables, to generate new dm-user
-// devices and to flush I/O.
-// (3) Kill snapuserd, which no longer has any dm-user devices to attach to.
-// (4) Load the sepolicy and issue critical restorecons in /dev, carefully
-// avoiding anything that would read from /system.
-// (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).
-//
-// After this sequence, it is safe to enable enforcing mode and continue booting.
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
@@ -723,31 +681,9 @@ int SetupSelinux(char** argv) {
MountMissingSystemPartitions();
+ // Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
-
- LOG(INFO) << "Opening SELinux policy";
-
- // Read the policy before potentially killing snapuserd.
- std::string policy;
- ReadPolicy(&policy);
-
- auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
- if (snapuserd_helper) {
- // Kill the old snapused to avoid audit messages. After this we cannot
- // read from /system (or other dynamic partitions) until we call
- // FinishTransition().
- snapuserd_helper->StartTransition();
- }
-
- LoadSelinuxPolicy(policy);
-
- if (snapuserd_helper) {
- // Before enforcing, finish the pending snapuserd transition.
- snapuserd_helper->FinishTransition();
- snapuserd_helper = nullptr;
- }
-
- SelinuxSetEnforcement();
+ SelinuxInitialize();
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
diff --git a/init/service.cpp b/init/service.cpp
index c3069f5b2..69f944eb2 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -45,14 +45,12 @@
#include <android/api-level.h>
#include "mount_namespace.h"
-#include "reboot_utils.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
using android::base::boot_clock;
-using android::base::GetBoolProperty;
using android::base::GetProperty;
using android::base::Join;
using android::base::make_scope_guard;
@@ -73,12 +71,12 @@ static Result<std::string> ComputeContextFromExecutable(const std::string& servi
if (getcon(&raw_con) == -1) {
return Error() << "Could not get security context";
}
- std::unique_ptr<char, decltype(&freecon)> mycon(raw_con, freecon);
+ std::unique_ptr<char> mycon(raw_con);
if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
return Error() << "Could not get file context";
}
- std::unique_ptr<char, decltype(&freecon)> filecon(raw_filecon, freecon);
+ std::unique_ptr<char> filecon(raw_filecon);
char* new_con = nullptr;
int rc = security_compute_create(mycon.get(), filecon.get(),
@@ -92,9 +90,7 @@ static Result<std::string> ComputeContextFromExecutable(const std::string& servi
<< "\") has incorrect label or no domain transition from " << mycon.get()
<< " to another SELinux domain defined. Have you configured your "
"service correctly? https://source.android.com/security/selinux/"
- "device-policy#label_new_services_and_address_denials. Note: this "
- "error shows up even in permissive mode in order to make auditing "
- "denials possible.";
+ "device-policy#label_new_services_and_address_denials";
}
if (rc < 0) {
return Error() << "Could not get process context";
@@ -125,6 +121,12 @@ static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigsto
return execv(c_strings[0], c_strings.data()) == 0;
}
+static bool AreRuntimeApexesReady() {
+ struct stat buf;
+ return stat("/apex/com.android.art/", &buf) == 0 &&
+ stat("/apex/com.android.runtime/", &buf) == 0;
+}
+
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
@@ -149,7 +151,6 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
.priority = 0},
namespaces_{.flags = namespace_flags},
seclabel_(seclabel),
- subcontext_(subcontext_for_restart_commands),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
@@ -307,28 +308,22 @@ void Service::Reap(const siginfo_t& siginfo) {
#else
static bool is_apex_updatable = false;
#endif
- const bool is_process_updatable = !use_bootstrap_ns_ && is_apex_updatable;
+ const bool is_process_updatable = !pre_apexd_ && is_apex_updatable;
- // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
+ // If we crash > 4 times in 4 minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
- bool boot_completed = GetBoolProperty("sys.boot_completed", false);
- if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
+ bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+ if (now < time_crashed_ + 4min || !boot_completed) {
if (++crash_count_ > 4) {
- auto exit_reason = boot_completed ?
- "in " + std::to_string(fatal_crash_window_.count()) + " minutes" :
- "before boot completed";
if (flags_ & SVC_CRITICAL) {
- if (!GetBoolProperty("init.svc_debug.no_fatal." + name_, false)) {
- // Aborts into `fatal_reboot_target_'.
- SetFatalRebootTarget(fatal_reboot_target_);
- LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
- << exit_reason;
- }
+ // Aborts into bootloader
+ LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+ << (boot_completed ? "in 4 minutes" : "before boot completed");
} else {
- LOG(ERROR) << "process with updatable components '" << name_
- << "' exited 4 times " << exit_reason;
+ LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+ << (boot_completed ? "in 4 minutes" : "before boot completed");
// Notifies update_verifier and apexd
SetProperty("sys.init.updatable_crashing_process_name", name_);
SetProperty("sys.init.updatable_crashing", "1");
@@ -460,26 +455,12 @@ Result<void> Service::Start() {
scon = *result;
}
- // APEXd is always started in the "current" namespace because it is the process to set up
- // the current namespace.
- const bool is_apexd = args_[0] == "/system/bin/apexd";
-
- if (!IsDefaultMountNamespaceReady() && !is_apexd) {
- // If this service is started before APEXes and corresponding linker configuration
- // get available, mark it as pre-apexd one. Note that this marking is
+ if (!AreRuntimeApexesReady() && !pre_apexd_) {
+ // If this service is started before the Runtime and ART APEXes get
+ // available, mark it as pre-apexd one. Note that this marking is
// permanent. So for example, if the service is re-launched (e.g., due
// to crash), it is still recognized as pre-apexd... for consistency.
- use_bootstrap_ns_ = true;
- }
-
- // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
- // Note: "ueventd" is supposed to be run in "default" mount namespace even if it's pre-apexd
- // to support loading firmwares from APEXes.
- std::optional<MountNamespace> override_mount_namespace;
- if (name_ == "ueventd") {
- override_mount_namespace = NS_DEFAULT;
- } else if (use_bootstrap_ns_) {
- override_mount_namespace = NS_BOOTSTRAP;
+ pre_apexd_ = true;
}
post_data_ = ServiceList::GetInstance().IsPostData();
@@ -513,8 +494,7 @@ Result<void> Service::Start() {
if (pid == 0) {
umask(077);
- if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
- !result.ok()) {
+ if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result.ok()) {
LOG(FATAL) << "Service '" << name_
<< "' failed to set up namespaces: " << result.error();
}
diff --git a/init/service.h b/init/service.h
index 043555fa4..34ed5eff9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -137,7 +137,6 @@ class Service {
flags_ &= ~SVC_ONESHOT;
}
}
- Subcontext* subcontext() const { return subcontext_; }
private:
void NotifyStateChange(const std::string& new_state) const;
@@ -156,8 +155,6 @@ class Service {
android::base::boot_clock::time_point time_started_; // time of last start
android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
- std::chrono::minutes fatal_crash_window_ = 4min; // fatal() when more than 4 crashes in it
- std::optional<std::string> fatal_reboot_target_; // reboot target of fatal handler
std::optional<CapSet> capabilities_;
ProcessAttributes proc_attr_;
@@ -169,7 +166,6 @@ class Service {
std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
- Subcontext* subcontext_;
Action onrestart_; // Commands to execute on restart.
std::vector<std::string> writepid_files_;
@@ -207,7 +203,7 @@ class Service {
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
- bool use_bootstrap_ns_ = false;
+ bool pre_apexd_ = false;
bool post_data_ = false;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 57c311a52..bdac0777b 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -93,39 +93,6 @@ Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {
}
Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
- std::optional<std::string> fatal_reboot_target;
- std::optional<std::chrono::minutes> fatal_crash_window;
-
- for (auto it = args.begin() + 1; it != args.end(); ++it) {
- auto arg = android::base::Split(*it, "=");
- if (arg.size() != 2) {
- return Error() << "critical: Argument '" << *it << "' is not supported";
- } else if (arg[0] == "target") {
- fatal_reboot_target = arg[1];
- } else if (arg[0] == "window") {
- int minutes;
- auto window = ExpandProps(arg[1]);
- if (!window.ok()) {
- return Error() << "critical: Could not expand argument ': " << arg[1];
- }
- if (*window == "off") {
- return {};
- }
- if (!ParseInt(*window, &minutes, 0)) {
- return Error() << "critical: 'fatal_crash_window' must be an integer > 0";
- }
- fatal_crash_window = std::chrono::minutes(minutes);
- } else {
- return Error() << "critical: Argument '" << *it << "' is not supported";
- }
- }
-
- if (fatal_reboot_target) {
- service_->fatal_reboot_target_ = *fatal_reboot_target;
- }
- if (fatal_crash_window) {
- service_->fatal_crash_window_ = *fatal_crash_window;
- }
service_->flags_ |= SVC_CRITICAL;
return {};
}
@@ -539,7 +506,7 @@ const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() con
{"capabilities", {0, kMax, &ServiceParser::ParseCapabilities}},
{"class", {1, kMax, &ServiceParser::ParseClass}},
{"console", {0, 1, &ServiceParser::ParseConsole}},
- {"critical", {0, 2, &ServiceParser::ParseCritical}},
+ {"critical", {0, 0, &ServiceParser::ParseCritical}},
{"disabled", {0, 0, &ServiceParser::ParseDisabled}},
{"enter_namespace", {2, 2, &ServiceParser::ParseEnterNamespace}},
{"file", {2, 2, &ServiceParser::ParseFile}},
@@ -657,14 +624,6 @@ Result<void> ServiceParser::EndSection() {
<< "' with a config in APEX";
}
- std::string context = service_->subcontext() ? service_->subcontext()->context() : "";
- std::string old_context =
- old_service->subcontext() ? old_service->subcontext()->context() : "";
- if (context != old_context) {
- return Error() << "service '" << service_->name() << "' overrides another service "
- << "across the treble boundary.";
- }
-
service_list_->RemoveService(*old_service);
old_service = nullptr;
}
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index f2383d7a6..484c2c89f 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -60,14 +60,13 @@ Result<void> EnterNamespace(int nstype, const char* path) {
Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // Recursively remount / as MS_SLAVE like zygote does so that
- // unmounting and mounting /proc doesn't interfere with the parent
- // namespace's /proc mount. This will also prevent any other
- // mounts/unmounts initiated by the service from interfering with the
- // parent namespace but will still allow mount events from the parent
+ // Recursively remount / as slave like zygote does so unmounting and mounting /proc
+ // doesn't interfere with the parent namespace's /proc mount. This will also
+ // prevent any other mounts/unmounts initiated by the service from interfering
+ // with the parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- return ErrnoError() << "Could not remount(/) recursively as MS_SLAVE";
+ return ErrnoError() << "Could not remount(/) recursively as slave";
}
// umount() then mount() /proc and/or /sys
@@ -195,8 +194,7 @@ Result<Descriptor> FileDescriptor::Create() const {
return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
}
-Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
- std::optional<MountNamespace> override_mount_namespace) {
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
for (const auto& [nstype, path] : info.namespaces_to_enter) {
if (auto result = EnterNamespace(nstype, path.c_str()); !result.ok()) {
return result;
@@ -204,10 +202,9 @@ Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
}
#if defined(__ANDROID__)
- if (override_mount_namespace.has_value()) {
- if (auto result = SwitchToMountNamespaceIfNeeded(override_mount_namespace.value());
- !result.ok()) {
- return result;
+ if (pre_apexd) {
+ if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ return Error() << "could not enter into the bootstrap mount namespace";
}
}
#endif
diff --git a/init/service_utils.h b/init/service_utils.h
index 1e0b4bd2b..3f1071e5b 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -19,14 +19,12 @@
#include <sys/resource.h>
#include <sys/types.h>
-#include <optional>
#include <string>
#include <vector>
#include <android-base/unique_fd.h>
#include <cutils/iosched_policy.h>
-#include "mount_namespace.h"
#include "result.h"
namespace android {
@@ -37,8 +35,6 @@ class Descriptor {
Descriptor(const std::string& name, android::base::unique_fd fd)
: name_(name), fd_(std::move(fd)){};
- // Publish() unsets FD_CLOEXEC from the FD and publishes its name via setenv(). It should be
- // called when starting a service after fork() and before exec().
void Publish() const;
private:
@@ -55,9 +51,6 @@ struct SocketDescriptor {
std::string context;
bool passcred = false;
- // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
- // It should be called when starting a service, before calling fork(), such that the socket is
- // synchronously created before starting any other services, which may depend on it.
Result<Descriptor> Create(const std::string& global_context) const;
};
@@ -73,8 +66,7 @@ struct NamespaceInfo {
// Pair of namespace type, path to name.
std::vector<std::pair<int, std::string>> namespaces_to_enter;
};
-Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
- std::optional<MountNamespace> override_mount_namespace);
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd);
struct ProcessAttributes {
std::string console;
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
deleted file mode 100644
index 40467b7d3..000000000
--- a/init/snapuserd_transition.cpp
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd_transition.h"
-
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/xattr.h>
-#include <unistd.h>
-
-#include <filesystem>
-#include <string>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/sockets.h>
-#include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapuserd_client.h>
-#include <private/android_filesystem_config.h>
-#include <procinfo/process_map.h>
-#include <selinux/android.h>
-
-#include "block_dev_initializer.h"
-#include "service_utils.h"
-#include "util.h"
-
-namespace android {
-namespace init {
-
-using namespace std::string_literals;
-
-using android::base::unique_fd;
-using android::snapshot::SnapshotManager;
-using android::snapshot::SnapuserdClient;
-
-static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
-static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
-static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
-static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
-static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
-
-void LaunchFirstStageSnapuserd() {
- SocketDescriptor socket_desc;
- socket_desc.name = android::snapshot::kSnapuserdSocket;
- socket_desc.type = SOCK_STREAM;
- socket_desc.perm = 0660;
- socket_desc.uid = AID_SYSTEM;
- socket_desc.gid = AID_SYSTEM;
-
- // We specify a label here even though it technically is not needed. During
- // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded,
- // we bypass the socket entirely.
- auto socket = socket_desc.Create(kSnapuserdSocketLabel);
- if (!socket.ok()) {
- LOG(FATAL) << "Could not create snapuserd socket: " << socket.error();
- }
-
- pid_t pid = fork();
- if (pid < 0) {
- PLOG(FATAL) << "Cannot launch snapuserd; fork failed";
- }
- if (pid == 0) {
- socket->Publish();
- char arg0[] = "/system/bin/snapuserd";
- char* const argv[] = {arg0, nullptr};
- if (execv(arg0, argv) < 0) {
- PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
- }
- _exit(127);
- }
-
- setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
-
- LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
-}
-
-std::optional<pid_t> GetSnapuserdFirstStagePid() {
- const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
- if (!pid_str) {
- return {};
- }
-
- int pid = 0;
- if (!android::base::ParseInt(pid_str, &pid)) {
- LOG(FATAL) << "Could not parse pid in environment, " << kSnapuserdFirstStagePidVar << "="
- << pid_str;
- }
- return {pid};
-}
-
-static void RelabelLink(const std::string& link) {
- selinux_android_restorecon(link.c_str(), 0);
-
- std::string path;
- if (android::base::Readlink(link, &path)) {
- selinux_android_restorecon(path.c_str(), 0);
- }
-}
-
-static void RelabelDeviceMapper() {
- selinux_android_restorecon("/dev/device-mapper", 0);
-
- std::error_code ec;
- for (auto& iter : std::filesystem::directory_iterator("/dev/block", ec)) {
- const auto& path = iter.path();
- if (android::base::StartsWith(path.string(), "/dev/block/dm-")) {
- selinux_android_restorecon(path.string().c_str(), 0);
- }
- }
-}
-
-static std::optional<int> GetRamdiskSnapuserdFd() {
- const char* fd_str = getenv(kSnapuserdFirstStageFdVar);
- if (!fd_str) {
- return {};
- }
-
- int fd;
- if (!android::base::ParseInt(fd_str, &fd)) {
- LOG(FATAL) << "Could not parse fd in environment, " << kSnapuserdFirstStageFdVar << "="
- << fd_str;
- }
- return {fd};
-}
-
-void RestoreconRamdiskSnapuserd(int fd) {
- if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) {
- PLOG(FATAL) << "fsetxattr snapuserd failed";
- }
-}
-
-SnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid)
- : sm_(std::move(sm)), old_pid_(old_pid) {
- // Only dm-user device names change during transitions, so the other
- // devices are expected to be present.
- sm_->SetUeventRegenCallback([this](const std::string& device) -> bool {
- if (android::base::StartsWith(device, "/dev/dm-user/")) {
- return block_dev_init_.InitDmUser(android::base::Basename(device));
- }
- return true;
- });
-}
-
-static void LockAllSystemPages() {
- bool ok = true;
- auto callback = [&](const android::procinfo::MapInfo& map) -> void {
- if (!ok || android::base::StartsWith(map.name, "/dev/") ||
- !android::base::StartsWith(map.name, "/")) {
- return;
- }
- auto start = reinterpret_cast<const void*>(map.start);
- auto len = map.end - map.start;
- if (!len) {
- return;
- }
- if (mlock(start, len) < 0) {
- LOG(ERROR) << "mlock failed, " << start << " for " << len << " bytes.";
- ok = false;
- }
- };
-
- if (!android::procinfo::ReadProcessMaps(getpid(), callback) || !ok) {
- LOG(FATAL) << "Could not process /proc/" << getpid() << "/maps file for init, "
- << "falling back to mlockall().";
- if (mlockall(MCL_CURRENT) < 0) {
- LOG(FATAL) << "mlockall failed";
- }
- }
-}
-
-void SnapuserdSelinuxHelper::StartTransition() {
- LOG(INFO) << "Starting SELinux transition of snapuserd";
-
- // The restorecon path reads from /system etc, so make sure any reads have
- // been cached before proceeding.
- auto handle = selinux_android_file_context_handle();
- if (!handle) {
- LOG(FATAL) << "Could not create SELinux file context handle";
- }
- selinux_android_set_sehandle(handle);
-
- // We cannot access /system after the transition, so make sure init is
- // pinned in memory.
- LockAllSystemPages();
-
- argv_.emplace_back("snapuserd");
- argv_.emplace_back("-no_socket");
- if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
- LOG(FATAL) << "Could not perform selinux transition";
- }
-
- // Make sure the process is gone so we don't have any selinux audits.
- KillFirstStageSnapuserd(old_pid_);
-}
-
-void SnapuserdSelinuxHelper::FinishTransition() {
- RelabelLink("/dev/block/by-name/super");
- RelabelDeviceMapper();
-
- selinux_android_restorecon("/dev/null", 0);
- selinux_android_restorecon("/dev/urandom", 0);
- selinux_android_restorecon("/dev/kmsg", 0);
- selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
-
- RelaunchFirstStageSnapuserd();
-
- if (munlockall() < 0) {
- PLOG(ERROR) << "munlockall failed";
- }
-}
-
-void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
- auto fd = GetRamdiskSnapuserdFd();
- if (!fd) {
- LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
- }
- unsetenv(kSnapuserdFirstStageFdVar);
-
- RestoreconRamdiskSnapuserd(fd.value());
-
- pid_t pid = fork();
- if (pid < 0) {
- PLOG(FATAL) << "Fork to relaunch snapuserd failed";
- }
- if (pid > 0) {
- // We don't need the descriptor anymore, and it should be closed to
- // avoid leaking into subprocesses.
- close(fd.value());
-
- setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
-
- LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
- return;
- }
-
- // Make sure the descriptor is gone after we exec.
- if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) {
- PLOG(FATAL) << "fcntl FD_CLOEXEC failed for snapuserd fd";
- }
-
- std::vector<char*> argv;
- for (auto& arg : argv_) {
- argv.emplace_back(arg.data());
- }
- argv.emplace_back(nullptr);
-
- int rv = syscall(SYS_execveat, fd.value(), "", reinterpret_cast<char* const*>(argv.data()),
- nullptr, AT_EMPTY_PATH);
- if (rv < 0) {
- PLOG(FATAL) << "Failed to execveat() snapuserd";
- }
-}
-
-std::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() {
- if (IsRecoveryMode()) {
- return nullptr;
- }
-
- auto old_pid = GetSnapuserdFirstStagePid();
- if (!old_pid) {
- return nullptr;
- }
-
- auto sm = SnapshotManager::NewForFirstStageMount();
- if (!sm) {
- LOG(FATAL) << "Unable to create SnapshotManager";
- }
- return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value());
-}
-
-void KillFirstStageSnapuserd(pid_t pid) {
- if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
- LOG(ERROR) << "Kill snapuserd pid failed: " << pid;
- } else {
- LOG(INFO) << "Sent SIGTERM to snapuserd process " << pid;
- }
-}
-
-void CleanupSnapuserdSocket() {
- auto socket_path = ANDROID_SOCKET_DIR "/"s + android::snapshot::kSnapuserdSocket;
- if (access(socket_path.c_str(), F_OK) != 0) {
- return;
- }
-
- // Tell the daemon to stop accepting connections and to gracefully exit
- // once all outstanding handlers have terminated.
- if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) {
- client->DetachSnapuserd();
- }
-
- // Unlink the socket so we can create it again in second-stage.
- if (unlink(socket_path.c_str()) < 0) {
- PLOG(FATAL) << "unlink " << socket_path << " failed";
- }
-}
-
-void SaveRamdiskPathToSnapuserd() {
- int fd = open(kSnapuserdPath, O_PATH);
- if (fd < 0) {
- PLOG(FATAL) << "Unable to open snapuserd: " << kSnapuserdPath;
- }
-
- auto value = std::to_string(fd);
- if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) {
- PLOG(FATAL) << "setenv failed: " << kSnapuserdFirstStageFdVar << "=" << value;
- }
-}
-
-bool IsFirstStageSnapuserdRunning() {
- return GetSnapuserdFirstStagePid().has_value();
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
deleted file mode 100644
index a5ab652b7..000000000
--- a/init/snapuserd_transition.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/types.h>
-
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <libsnapshot/snapshot.h>
-
-#include "block_dev_initializer.h"
-
-namespace android {
-namespace init {
-
-// Fork and exec a new copy of snapuserd.
-void LaunchFirstStageSnapuserd();
-
-class SnapuserdSelinuxHelper final {
- using SnapshotManager = android::snapshot::SnapshotManager;
-
- public:
- SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid);
-
- void StartTransition();
- void FinishTransition();
-
- // Return a helper for facilitating the selinux transition of snapuserd.
- // If snapuserd is not in use, null is returned. StartTransition() should
- // be called after reading policy. FinishTransition() should be called
- // after loading policy. In between, no reads of /system or other dynamic
- // partitions are possible.
- static std::unique_ptr<SnapuserdSelinuxHelper> CreateIfNeeded();
-
- private:
- void RelaunchFirstStageSnapuserd();
- void ExecSnapuserd();
-
- std::unique_ptr<SnapshotManager> sm_;
- BlockDevInitializer block_dev_init_;
- pid_t old_pid_;
- std::vector<std::string> argv_;
-};
-
-// Remove /dev/socket/snapuserd. This ensures that (1) the existing snapuserd
-// will receive no new requests, and (2) the next copy we transition to can
-// own the socket.
-void CleanupSnapuserdSocket();
-
-// Kill an instance of snapuserd given a pid.
-void KillFirstStageSnapuserd(pid_t pid);
-
-// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
-// used to later execveat() snapuserd.
-void SaveRamdiskPathToSnapuserd();
-
-// Returns true if first-stage snapuserd is running.
-bool IsFirstStageSnapuserdRunning();
-
-// Return the pid of the first-stage instances of snapuserd, if it was started.
-std::optional<pid_t> GetSnapuserdFirstStagePid();
-
-// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
-// used to later execveat() snapuserd.
-void SaveRamdiskPathToSnapuserd();
-
-// Returns true if first-stage snapuserd is running.
-bool IsFirstStageSnapuserdRunning();
-
-} // namespace init
-} // namespace android
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index fa48beab3..f3dd53826 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,8 +18,6 @@
#include <fcntl.h>
#include <poll.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include <unistd.h>
#include <android-base/file.h>
@@ -30,7 +28,6 @@
#include "action.h"
#include "builtins.h"
-#include "mount_namespace.h"
#include "proto_utils.h"
#include "util.h"
@@ -184,8 +181,6 @@ int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map
trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
- // Restore prio before main loop
- setpriority(PRIO_PROCESS, 0, 0);
subcontext_process.MainLoop();
return 0;
}
@@ -218,13 +213,7 @@ void Subcontext::Fork() {
PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
}
}
-#if defined(__ANDROID__)
- // subcontext init runs in "default" mount namespace
- // so that it can access /apex/*
- if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
- LOG(FATAL) << "Could not switch to \"default\" mount namespace: " << result.error();
- }
-#endif
+
auto init_path = GetExecutablePath();
auto child_fd_string = std::to_string(child_fd);
const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
@@ -342,18 +331,12 @@ void InitializeSubcontext() {
new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
}
-void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes) {
- subcontext.reset(new Subcontext(vendor_prefixes, kVendorContext, /*host=*/true));
-}
Subcontext* GetSubcontext() {
return subcontext.get();
}
bool SubcontextChildReap(pid_t pid) {
- if (!subcontext) {
- return false;
- }
if (subcontext->pid() == pid) {
if (!subcontext_terminated_by_shutdown) {
subcontext->Restart();
diff --git a/init/subcontext.h b/init/subcontext.h
index cb4138e69..788d3be6c 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,11 +36,9 @@ static constexpr const char kTestContext[] = "test-test-test";
class Subcontext {
public:
- Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
+ Subcontext(std::vector<std::string> path_prefixes, std::string context)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
- if (!host) {
- Fork();
- }
+ Fork();
}
Result<void> Execute(const std::vector<std::string>& args);
@@ -63,7 +61,6 @@ class Subcontext {
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
void InitializeSubcontext();
-void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes);
Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index da1f45550..ee765a7a7 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -202,8 +202,6 @@ BuiltinFunctionMap BuildTestFunctionMap() {
// For RecoverAfterAbort
auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {
- // Since this is an expected failure, disable debuggerd to not generate a tombstone.
- signal(SIGABRT, SIG_DFL);
return Error() << std::string(4097, 'f');
};
auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {
diff --git a/init/sysprop/Android.bp b/init/sysprop/Android.bp
index 296cdc16d..7582875c5 100644
--- a/init/sysprop/Android.bp
+++ b/init/sysprop/Android.bp
@@ -1,12 +1,3 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_core_init_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_core_init_license"],
-}
-
sysprop_library {
name: "com.android.sysprop.init",
srcs: ["InitProperties.sysprop"],
diff --git a/init/sysprop/api/com.android.sysprop.init-latest.txt b/init/sysprop/api/com.android.sysprop.init-latest.txt
index 01f4e9a91..c835b95cf 100644
--- a/init/sysprop/api/com.android.sysprop.init-latest.txt
+++ b/init/sysprop/api/com.android.sysprop.init-latest.txt
@@ -1,13 +1,8 @@
props {
module: "android.sysprop.InitProperties"
prop {
- api_name: "is_userspace_reboot_supported"
- prop_name: "init.userspace_reboot.is_supported"
- integer_as_bool: true
- }
- prop {
api_name: "userspace_reboot_in_progress"
- access: ReadWrite
+ scope: Public
prop_name: "sys.init.userspace_reboot.in_progress"
integer_as_bool: true
}
diff --git a/init/test_kill_services/Android.bp b/init/test_kill_services/Android.bp
deleted file mode 100644
index 37361a813..000000000
--- a/init/test_kill_services/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_core_init_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_core_init_license"],
-}
-
-cc_test {
- name: "init_kill_services_test",
- srcs: ["init_kill_services_test.cpp"],
- shared_libs: ["libbase"],
- test_suites: ["general-tests"],
-
- // TODO(b/153565474): switch back to auto-generation
- // and add back:
- // require_root: true,
- auto_gen_config: false,
-}
diff --git a/init/test_kill_services/AndroidTest.xml b/init/test_kill_services/AndroidTest.xml
deleted file mode 100644
index 8018efafb..000000000
--- a/init/test_kill_services/AndroidTest.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs init_kill_services_test.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
-
- <!-- cannot be autogenerated: b/153565474 -->
- <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
- <!-- flake mitigation, in case device is in bad state-->
- <option name="pre-reboot" value="true" />
- <!-- sometimes device gets into bad state, and we don't detect it in this test,
- so the test succeeds and the next test fails. This is a really bad result, so
- to avoid that, making sure we reboot the device again before running any more
- tests.
- TODO(b/152556737): add metrics for successful device recovery -->
- <option name="post-reboot" value="true" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="init_kill_services_test->/data/local/tmp/init_kill_services_test" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="init_kill_services_test" />
- </test>
-</configuration>
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
deleted file mode 100644
index 66a332892..000000000
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <android-base/properties.h>
-
-#include <iostream>
-
-using ::android::base::GetProperty;
-using ::android::base::SetProperty;
-
-void ExpectKillingServiceRecovers(const std::string& service_name) {
- const std::string status_prop = "init.svc." + service_name;
- const std::string pid_prop = "init.svc_debug_pid." + service_name;
-
- const std::string initial_pid = GetProperty(pid_prop, "");
-
- EXPECT_EQ("running", GetProperty(status_prop, "")) << status_prop;
- EXPECT_NE("", initial_pid) << pid_prop;
-
- EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
-
- constexpr size_t kMaxWaitMilliseconds = 10000;
- constexpr size_t kRetryWaitMilliseconds = 100;
-
- constexpr size_t kRetryTimes = kMaxWaitMilliseconds / kRetryWaitMilliseconds;
-
- for (size_t retry = 0; retry < kRetryTimes; retry++) {
- const std::string& pid = GetProperty(pid_prop, "");
- if (pid != initial_pid && pid != "") break;
- usleep(kRetryWaitMilliseconds * 1000);
- }
-
- // svc_debug_pid is set after svc property
- EXPECT_EQ("running", GetProperty(status_prop, ""));
-}
-
-class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
-
-TEST_P(InitKillServicesTest, KillCriticalProcesses) {
- ExpectKillingServiceRecovers(GetParam());
-
- // Ensure that init is still responding
- EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
- EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
- EXPECT_TRUE(SetProperty("test.death.test", ""));
-}
-
-static inline std::string PrintName(const testing::TestParamInfo<std::string>& info) {
- return info.param;
-}
-
-INSTANTIATE_TEST_CASE_P(DeathTest, InitKillServicesTest,
- ::testing::Values("lmkd", "ueventd", "hwservicemanager", "servicemanager"),
- PrintName);
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
index 37bc37116..8bd16a74f 100644
--- a/init/test_service/Android.bp
+++ b/init/test_service/Android.bp
@@ -14,15 +14,6 @@
// limitations under the License.
//
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_core_init_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_core_init_license"],
-}
-
cc_binary {
name: "test_service",
srcs: ["test_service.cpp"],
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
index 012288458..6b31683e0 100644
--- a/init/tokenizer_test.cpp
+++ b/init/tokenizer_test.cpp
@@ -28,7 +28,7 @@ namespace {
void RunTest(const std::string& data, const std::vector<std::vector<std::string>>& expected_tokens) {
auto data_copy = std::string{data};
- data_copy.push_back('\n');
+ data_copy.push_back('\n'); // TODO: fix tokenizer
data_copy.push_back('\0');
parse_state state;
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 7cd396a8b..d8d9b36ff 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -95,18 +95,20 @@ UeventListener::UeventListener(size_t uevent_socket_rcvbuf_size) {
fcntl(device_fd_, F_SETFL, O_NONBLOCK);
}
-ReadUeventResult UeventListener::ReadUevent(Uevent* uevent) const {
+bool UeventListener::ReadUevent(Uevent* uevent) const {
char msg[UEVENT_MSG_LEN + 2];
int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
if (n <= 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
PLOG(ERROR) << "Error reading from Uevent Fd";
}
- return ReadUeventResult::kFailed;
+ return false;
}
if (n >= UEVENT_MSG_LEN) {
LOG(ERROR) << "Uevent overflowed buffer, discarding";
- return ReadUeventResult::kInvalid;
+ // Return true here even if we discard as we may have more uevents pending and we
+ // want to keep processing them.
+ return true;
}
msg[n] = '\0';
@@ -114,7 +116,7 @@ ReadUeventResult UeventListener::ReadUevent(Uevent* uevent) const {
ParseEvent(msg, uevent);
- return ReadUeventResult::kSuccess;
+ return true;
}
// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
@@ -135,10 +137,7 @@ ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
close(fd);
Uevent uevent;
- ReadUeventResult result;
- while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {
- // Skip processing the uevent if it is invalid.
- if (result == ReadUeventResult::kInvalid) continue;
+ while (ReadUevent(&uevent)) {
if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
}
}
@@ -213,10 +212,7 @@ void UeventListener::Poll(const ListenerCallback& callback,
// We're non-blocking, so if we receive a poll event keep processing until
// we have exhausted all uevent messages.
Uevent uevent;
- ReadUeventResult result;
- while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {
- // Skip processing the uevent if it is invalid.
- if (result == ReadUeventResult::kInvalid) continue;
+ while (ReadUevent(&uevent)) {
if (callback(uevent) == ListenerAction::kStop) return;
}
}
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index 277286064..aea094e77 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -27,7 +27,7 @@
#include "uevent.h"
-#define UEVENT_MSG_LEN 8192
+#define UEVENT_MSG_LEN 2048
namespace android {
namespace init {
@@ -37,12 +37,6 @@ enum class ListenerAction {
kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
};
-enum class ReadUeventResult {
- kSuccess = 0, // Uevent was successfully read.
- kFailed, // Uevent reading has failed.
- kInvalid, // An Invalid Uevent was read (like say, the msg received is >= UEVENT_MSG_LEN).
-};
-
using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
class UeventListener {
@@ -56,7 +50,7 @@ class UeventListener {
const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
private:
- ReadUeventResult ReadUevent(Uevent* uevent) const;
+ bool ReadUevent(Uevent* uevent) const;
ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
android::base::unique_fd device_fd_;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 331255b1e..d2b503b4f 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -16,7 +16,6 @@
#include "ueventd.h"
-#include <android/api-level.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
@@ -267,17 +266,6 @@ void ColdBoot::Run() {
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
-static UeventdConfiguration GetConfiguration() {
- // TODO: Remove these legacy paths once Android S is no longer supported.
- if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
- auto hardware = android::base::GetProperty("ro.hardware", "");
- return ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
- "/ueventd." + hardware + ".rc"});
- }
-
- return ParseConfig({"/system/etc/ueventd.rc"});
-}
-
int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
@@ -295,7 +283,13 @@ int ueventd_main(int argc, char** argv) {
std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
- auto ueventd_configuration = GetConfiguration();
+ // Keep the current product name base configuration so we remain backwards compatible and
+ // allow it to override everything.
+ // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
+ auto hardware = android::base::GetProperty("ro.hardware", "");
+
+ auto ueventd_configuration = ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc",
+ "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
std::move(ueventd_configuration.dev_permissions),
@@ -328,8 +322,6 @@ int ueventd_main(int argc, char** argv) {
while (waitpid(-1, nullptr, WNOHANG) > 0) {
}
- // Restore prio before main loop
- setpriority(PRIO_PROCESS, 0, 0);
uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
for (auto& uevent_handler : uevent_handlers) {
uevent_handler->HandleUevent(uevent);
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 2221228d1..09dce4481 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -21,7 +21,6 @@
#include <android-base/parseint.h>
-#include "import_parser.h"
#include "keyword_map.h"
#include "parser.h"
@@ -34,12 +33,12 @@ Result<void> ParsePermissionsLine(std::vector<std::string>&& args,
std::vector<SysfsPermissions>* out_sysfs_permissions,
std::vector<Permissions>* out_dev_permissions) {
bool is_sysfs = out_sysfs_permissions != nullptr;
- if (is_sysfs && !(args.size() == 5 || args.size() == 6)) {
- return Error() << "/sys/ lines must have 5 or 6 entries";
+ if (is_sysfs && args.size() != 5) {
+ return Error() << "/sys/ lines must have 5 entries";
}
- if (!is_sysfs && !(args.size() == 4 || args.size() == 5)) {
- return Error() << "/dev/ lines must have 4 or 5 entries";
+ if (!is_sysfs && args.size() != 4) {
+ return Error() << "/dev/ lines must have 4 entries";
}
auto it = args.begin();
@@ -70,19 +69,10 @@ Result<void> ParsePermissionsLine(std::vector<std::string>&& args,
}
gid_t gid = grp->gr_gid;
- bool no_fnm_pathname = false;
- if (it != args.end()) {
- std::string& flags = *it++;
- if (flags != "no_fnm_pathname") {
- return Error() << "invalid option '" << flags << "', only no_fnm_pathname is supported";
- }
- no_fnm_pathname = true;
- }
-
if (is_sysfs) {
- out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid, no_fnm_pathname);
+ out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
} else {
- out_dev_permissions->emplace_back(name, perm, uid, gid, no_fnm_pathname);
+ out_dev_permissions->emplace_back(name, perm, uid, gid);
}
return {};
}
@@ -106,10 +96,10 @@ Result<void> ParseExternalFirmwareHandlerLine(
}
if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
- [&args](const auto& other) { return other.devpath == args[1]; }) !=
+ [&args](const auto& other) { return other.devpath == args[2]; }) !=
external_firmware_handlers->end()) {
return Error() << "found a previous external_firmware_handler with the same devpath, '"
- << args[1] << "'";
+ << args[2] << "'";
}
passwd* pwd = getpwnam(args[2].c_str());
@@ -234,7 +224,6 @@ UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
Parser parser;
UeventdConfiguration ueventd_configuration;
- parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
parser.AddSectionParser("subsystem",
std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index c5aa9e3b5..172ba0b38 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -45,13 +45,6 @@ void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissio
EXPECT_EQ(expected.attribute_, test.attribute_);
}
-void TestExternalFirmwareHandler(const ExternalFirmwareHandler& expected,
- const ExternalFirmwareHandler& test) {
- EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
- EXPECT_EQ(expected.uid, test.uid) << expected.uid;
- EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
-}
-
template <typename T, typename F>
void TestVector(const T& expected, const T& test, F function) {
ASSERT_EQ(expected.size(), test.size());
@@ -74,8 +67,6 @@ void TestUeventdFile(const std::string& content, const UeventdConfiguration& exp
TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);
TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);
EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
- TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,
- TestExternalFirmwareHandler);
}
TEST(ueventd_parser, EmptyFile) {
@@ -113,21 +104,21 @@ TEST(ueventd_parser, Permissions) {
/dev/graphics/* 0660 root graphics
/dev/*/test 0660 root system
-/sys/devices/platform/trusty.* trusty_version 0440 root log
-/sys/devices/virtual/input/input enable 0660 root input
-/sys/devices/virtual/*/input poll_delay 0660 root input no_fnm_pathname
+/sys/devices/platform/trusty.* trusty_version 0440 root log
+/sys/devices/virtual/input/input enable 0660 root input
+/sys/devices/virtual/*/input poll_delay 0660 root input
)";
auto permissions = std::vector<Permissions>{
- {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
- {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
- {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM, false},
+ {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+ {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+ {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
};
auto sysfs_permissions = std::vector<SysfsPermissions>{
- {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG, false},
- {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT, false},
- {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
+ {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+ {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+ {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
};
TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
@@ -153,10 +144,7 @@ TEST(ueventd_parser, ExternalFirmwareHandlers) {
auto ueventd_file = R"(
external_firmware_handler devpath root handler_path
external_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh
-external_firmware_handler /devices/path/firmware/something002.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
-external_firmware_handler /devices/path/firmware/* root "/vendor/bin/firmware_handler.sh"
-external_firmware_handler /devices/path/firmware/something* system "/vendor/bin/firmware_handler.sh"
-external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor/bin/firmware_handler.sh"
+external_firmware_handler /devices/path/firmware/something001.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
)";
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
@@ -171,25 +159,10 @@ external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor
"/vendor/bin/firmware_handler.sh",
},
{
- "/devices/path/firmware/something002.bin",
+ "/devices/path/firmware/something001.bin",
AID_RADIO,
"/vendor/bin/firmware_handler.sh --has --arguments",
},
- {
- "/devices/path/firmware/",
- AID_ROOT,
- "/vendor/bin/firmware_handler.sh",
- },
- {
- "/devices/path/firmware/something",
- AID_SYSTEM,
- "/vendor/bin/firmware_handler.sh",
- },
- {
- "/devices/path/*/firmware/something*.bin",
- AID_RADIO,
- "/vendor/bin/firmware_handler.sh",
- },
};
TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
@@ -267,7 +240,7 @@ subsystem test_devpath_dirname
dirname /dev/graphics
/dev/*/test 0660 root system
-/sys/devices/virtual/*/input poll_delay 0660 root input no_fnm_pathname
+/sys/devices/virtual/*/input poll_delay 0660 root input
firmware_directories /more
external_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh
@@ -286,15 +259,15 @@ parallel_restorecon enabled
{"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
auto permissions = std::vector<Permissions>{
- {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
- {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
- {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM, false},
+ {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+ {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+ {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
};
auto sysfs_permissions = std::vector<SysfsPermissions>{
- {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG, false},
- {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT, false},
- {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
+ {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+ {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+ {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
};
auto firmware_directories = std::vector<std::string>{
@@ -326,7 +299,6 @@ firmware_directories #no directory listed
/sys/devices/platform/trusty.* trusty_version badmode root log
/sys/devices/platform/trusty.* trusty_version 0440 baduidbad log
/sys/devices/platform/trusty.* trusty_version 0440 root baduidbad
-/sys/devices/platform/trusty.* trusty_version 0440 root root bad_option
uevent_socket_rcvbuf_size blah
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index fc3cdfb7d..2d7d2f819 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -53,7 +53,11 @@ void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_
};
std::vector<std::thread> threads;
- for (const auto& [file, parameter] : files_and_parameters) {
+ // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
+ // for (const auto& [file, parameter] : files_and_parameters) {
+ for (const auto& pair : files_and_parameters) {
+ const auto& file = pair.first;
+ const auto& parameter = pair.second;
threads.emplace_back(std::thread(make_thread_function(file, parameter)));
}
diff --git a/init/util.cpp b/init/util.cpp
index 9f7bfdb5b..255434a1b 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -246,21 +246,6 @@ void ImportKernelCmdline(const std::function<void(const std::string&, const std:
}
}
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
- std::string bootconfig;
- android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
-
- for (const auto& entry : android::base::Split(bootconfig, "\n")) {
- std::vector<std::string> pieces = android::base::Split(entry, "=");
- if (pieces.size() == 2) {
- // get rid of the extra space between a list of values and remove the quotes.
- std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
- value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
- fn(android::base::Trim(pieces[0]), android::base::Trim(value));
- }
- }
-}
-
bool make_dir(const std::string& path, mode_t mode) {
std::string secontext;
if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
@@ -378,15 +363,6 @@ static std::string init_android_dt_dir() {
android_dt_dir = value;
}
});
- // ..Or bootconfig
- if (android_dt_dir == kDefaultAndroidDtDir) {
- ImportBootconfig([&](const std::string& key, const std::string& value) {
- if (key == "androidboot.android_dt_dir") {
- android_dt_dir = value;
- }
- });
- }
-
LOG(INFO) << "Using Android DT directory " << android_dt_dir;
return android_dt_dir;
}
@@ -746,16 +722,5 @@ bool IsRecoveryMode() {
return access("/system/bin/recovery", F_OK) == 0;
}
-// Check if default mount namespace is ready to be used with APEX modules
-static bool is_default_mount_namespace_ready = false;
-
-bool IsDefaultMountNamespaceReady() {
- return is_default_mount_namespace_ready;
-}
-
-void SetDefaultMountNamespaceReady() {
- is_default_mount_namespace_ready = true;
-}
-
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index daba85247..3cdc9f408 100644
--- a/init/util.h
+++ b/init/util.h
@@ -55,7 +55,6 @@ Result<uid_t> DecodeUid(const std::string& name);
bool mkdir_recursive(const std::string& pathname, mode_t mode);
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
bool make_dir(const std::string& path, mode_t mode);
bool is_dir(const char* pathname);
Result<std::string> ExpandProps(const std::string& src);
@@ -100,8 +99,5 @@ Result<std::string> ParseUmountAll(const std::vector<std::string>& args);
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
bool IsRecoveryMode();
-
-bool IsDefaultMountNamespaceReady();
-void SetDefaultMountNamespaceReady();
} // namespace init
} // namespace android
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 565e7d4f0..08343dcb5 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -61,8 +61,9 @@ TEST(util, ReadFileWorldWiteable) {
TEST(util, ReadFileSymbolicLink) {
errno = 0;
- // lrwxr-xr-x 1 root shell 6 2009-01-01 09:00 /system/bin/ps -> toybox
+ // lrwxrwxrwx 1 root shell 6 2020-06-26 09:55 /system/bin/ps -> toybox
auto file_contents = ReadFile("/system/bin/ps");
+
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents.ok());
EXPECT_EQ("open() failed: Too many symbolic links encountered",
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index a9bd94ec2..ae1481f72 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -1,9 +1,5 @@
// Copyright 2016 The Android Open Source Project
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "libappfuse_defaults",
local_include_dirs: ["include"],
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index ea98ae2bd..98e36652a 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -167,7 +167,7 @@ TEST_F(FuseAppLoopTest, LookUp) {
EXPECT_EQ(0u, response_.entry_out.attr.gid);
EXPECT_EQ(0u, response_.entry_out.attr.rdev);
EXPECT_EQ(0u, response_.entry_out.attr.blksize);
- EXPECT_EQ(0u, response_.entry_out.attr.flags);
+ EXPECT_EQ(0u, response_.entry_out.attr.padding);
}
TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
@@ -226,7 +226,7 @@ TEST_F(FuseAppLoopTest, GetAttr) {
EXPECT_EQ(0u, response_.attr_out.attr.gid);
EXPECT_EQ(0u, response_.attr_out.attr.rdev);
EXPECT_EQ(0u, response_.attr_out.attr.blksize);
- EXPECT_EQ(0u, response_.attr_out.attr.flags);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
}
TEST_F(FuseAppLoopTest, GetAttr_Root) {
@@ -259,7 +259,7 @@ TEST_F(FuseAppLoopTest, GetAttr_Root) {
EXPECT_EQ(0u, response_.attr_out.attr.gid);
EXPECT_EQ(0u, response_.attr_out.attr.rdev);
EXPECT_EQ(0u, response_.attr_out.attr.blksize);
- EXPECT_EQ(0u, response_.attr_out.attr.flags);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
}
TEST_F(FuseAppLoopTest, Open) {
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 692e223a1..44e7933c3 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "libasyncio_defaults",
cflags: [
diff --git a/libbacktrace/.clang-format b/libbacktrace/.clang-format
new file mode 120000
index 000000000..fd0645fdf
--- /dev/null
+++ b/libbacktrace/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2 \ No newline at end of file
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
new file mode 100644
index 000000000..c9e45124a
--- /dev/null
+++ b/libbacktrace/Android.bp
@@ -0,0 +1,230 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libbacktrace_common",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+libbacktrace_sources = [
+ "Backtrace.cpp",
+ "BacktraceCurrent.cpp",
+ "BacktracePtrace.cpp",
+ "ThreadEntry.cpp",
+ "UnwindStack.cpp",
+ "UnwindStackMap.cpp",
+]
+
+cc_library_headers {
+ name: "libbacktrace_headers",
+ vendor_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ export_include_dirs: ["include"],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "apex_inherit",
+}
+
+cc_defaults {
+ name: "libbacktrace_defaults",
+ defaults: ["libbacktrace_common"],
+
+ cflags: [
+ "-Wexit-time-destructors",
+ ],
+
+ srcs: [
+ "BacktraceMap.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+
+ target: {
+ darwin: {
+ enabled: true,
+ shared_libs: [
+ "libbase",
+ ],
+ },
+ linux: {
+ srcs: libbacktrace_sources,
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libprocinfo",
+ ],
+ },
+ android: {
+ static_libs: ["libasync_safe"],
+ static: {
+ whole_static_libs: ["libasync_safe"],
+ },
+ },
+ },
+}
+
+cc_library {
+ name: "libbacktrace",
+ vendor_available: false,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+ recovery_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ host_supported: true,
+ defaults: ["libbacktrace_defaults"],
+
+ target: {
+ linux: {
+ shared_libs: [
+ "libunwindstack",
+ ],
+ },
+ vendor: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ },
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ },
+ native_bridge: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ },
+ },
+}
+
+// Static library without DEX support to avoid dependencies on the ART APEX.
+cc_library_static {
+ name: "libbacktrace_no_dex",
+ visibility: ["//system/core/debuggerd"],
+ defaults: ["libbacktrace_defaults"],
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ target: {
+ linux: {
+ static_libs: [
+ "libunwindstack_no_dex",
+ ],
+ },
+ },
+}
+
+cc_test_library {
+ name: "libbacktrace_test",
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ strip: {
+ none: true,
+ },
+ cflags: ["-O0"],
+ srcs: ["backtrace_testlib.cpp"],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+ relative_install_path: "backtrace_test_libs",
+
+ target: {
+ linux_glibc: {
+ // This forces the creation of eh_frame with unwind information
+ // for host.
+ cflags: [
+ "-fcxx-exceptions"
+ ],
+ },
+ },
+}
+
+//-------------------------------------------------------------------------
+// The backtrace_test executable.
+//-------------------------------------------------------------------------
+cc_test {
+ name: "backtrace_test",
+ isolated: true,
+ defaults: ["libbacktrace_common"],
+ host_supported: true,
+ srcs: [
+ "backtrace_test.cpp",
+ ],
+
+ cflags: [
+ "-fno-builtin",
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "liblog",
+ "libunwindstack",
+ ],
+
+ group_static_libs: true,
+
+ // So that the dlopen can find the libbacktrace_test.so.
+ ldflags: [
+ "-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
+ ],
+
+ test_suites: ["device-tests"],
+ data: [
+ "testdata/arm/*",
+ "testdata/arm64/*",
+ "testdata/x86/*",
+ "testdata/x86_64/*",
+ ],
+ required: [
+ "libbacktrace_test",
+ ],
+}
+
+cc_benchmark {
+ name: "backtrace_benchmarks",
+ defaults: ["libbacktrace_common"],
+
+ srcs: [
+ "backtrace_benchmarks.cpp",
+ "backtrace_read_benchmarks.cpp",
+ ],
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libunwindstack",
+ ],
+}
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
new file mode 100644
index 000000000..3e050ab68
--- /dev/null
+++ b/libbacktrace/Backtrace.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "UnwindStack.h"
+
+using android::base::StringPrintf;
+
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
+//-------------------------------------------------------------------------
+// Backtrace functions.
+//-------------------------------------------------------------------------
+Backtrace::Backtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : pid_(pid), tid_(tid), map_(map), map_shared_(true) {
+ if (map_ == nullptr) {
+ map_ = BacktraceMap::Create(pid);
+ map_shared_ = false;
+ }
+}
+
+Backtrace::~Backtrace() {
+ if (map_ && !map_shared_) {
+ delete map_;
+ map_ = nullptr;
+ }
+}
+
+std::string Backtrace::GetFunctionName(uint64_t pc, uint64_t* offset, const backtrace_map_t* map) {
+ backtrace_map_t map_value;
+ if (map == nullptr) {
+ FillInMap(pc, &map_value);
+ map = &map_value;
+ }
+ // If no map is found, or this map is backed by a device, then return nothing.
+ if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
+ return "";
+ }
+ std::string name(GetFunctionNameRaw(pc, offset));
+ char* demangled_name = __cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name != nullptr) {
+ name = demangled_name;
+ free(demangled_name);
+ return name;
+ }
+ return name;
+}
+
+bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
+ if (ptr & (sizeof(word_t)-1)) {
+ BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+ return true;
+}
+
+std::string Backtrace::FormatFrameData(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrameData(&frames_[frame_num]);
+}
+
+std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
+ std::string map_name;
+ if (BacktraceMap::IsValid(frame->map)) {
+ map_name = frame->map.Name();
+ if (!frame->map.name.empty()) {
+ if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
+ map_name.resize(map_name.size() - 1);
+ map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
+ }
+ }
+ } else {
+ map_name = "<unknown>";
+ }
+
+ std::string line(StringPrintf("#%02zu pc %" PRIPTR " ", frame->num, frame->rel_pc));
+ line += map_name;
+ // Special handling for non-zero offset maps, we need to print that
+ // information.
+ if (frame->map.offset != 0) {
+ line += " (offset " + StringPrintf("0x%" PRIx64, frame->map.offset) + ")";
+ }
+ if (!frame->func_name.empty()) {
+ line += " (" + frame->func_name;
+ if (frame->func_offset) {
+ line += StringPrintf("+%" PRIu64, frame->func_offset);
+ }
+ line += ')';
+ }
+
+ return line;
+}
+
+void Backtrace::FillInMap(uint64_t pc, backtrace_map_t* map) {
+ if (map_ != nullptr) {
+ map_->FillIn(pc, map);
+ }
+}
+
+Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
+ if (pid == BACKTRACE_CURRENT_PROCESS) {
+ pid = getpid();
+ if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = android::base::GetThreadId();
+ }
+ } else if (tid == BACKTRACE_CURRENT_THREAD) {
+ tid = pid;
+ }
+
+ if (pid == getpid()) {
+ return new UnwindStackCurrent(pid, tid, map);
+ } else {
+ return new UnwindStackPtrace(pid, tid, map);
+ }
+}
+
+std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
+ switch (error.error_code) {
+ case BACKTRACE_UNWIND_NO_ERROR:
+ return "No error";
+ case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+ return "Setup failed";
+ case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+ return "No map found";
+ case BACKTRACE_UNWIND_ERROR_INTERNAL:
+ return "Internal libbacktrace error, please submit a bugreport";
+ case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+ return "Thread doesn't exist";
+ case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+ return "Thread has not responded to signal in time";
+ case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+ return "Attempt to use an unsupported feature";
+ case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+ return "Attempt to do an offline unwind without a context";
+ case BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT:
+ return "Exceed MAX_BACKTRACE_FRAMES limit";
+ case BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED:
+ return android::base::StringPrintf("Failed to read memory at addr 0x%" PRIx64,
+ error.error_info.addr);
+ case BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED:
+ return android::base::StringPrintf("Failed to read register %" PRIu64, error.error_info.regno);
+ case BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED:
+ return "Failed to find a function in debug sections";
+ case BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED:
+ return "Failed to execute dwarf instructions in debug sections";
+ case BACKTRACE_UNWIND_ERROR_UNWIND_INFO:
+ return "Failed to unwind due to invalid unwind information";
+ case BACKTRACE_UNWIND_ERROR_REPEATED_FRAME:
+ return "Failed to unwind due to same sp/pc repeating";
+ case BACKTRACE_UNWIND_ERROR_INVALID_ELF:
+ return "Failed to unwind due to invalid elf";
+ }
+}
diff --git a/libbacktrace/BacktraceAsyncSafeLog.h b/libbacktrace/BacktraceAsyncSafeLog.h
new file mode 100644
index 000000000..14f51be24
--- /dev/null
+++ b/libbacktrace/BacktraceAsyncSafeLog.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
+
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+// Logging macros for use in signal handler, only available on target.
+#define BACK_ASYNC_SAFE_LOGW(format, ...) \
+ async_safe_format_log(ANDROID_LOG_WARN, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+ ##__VA_ARGS__)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...) \
+ async_safe_format_log(ANDROID_LOG_ERROR, "libbacktrace", "%s: " format, __PRETTY_FUNCTION__, \
+ ##__VA_ARGS__)
+
+#else
+
+#define BACK_ASYNC_SAFE_LOGW(format, ...)
+
+#define BACK_ASYNC_SAFE_LOGE(format, ...)
+
+#endif
+
+#endif // _LIBBACKTRACE_BACKTRACE_ASYNC_SAFE_LOG_H
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
new file mode 100644
index 000000000..038b59e15
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <stdlib.h>
+
+#include <string>
+
+#include <android-base/threads.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceAsyncSafeLog.h"
+#include "BacktraceCurrent.h"
+#include "ThreadEntry.h"
+
+bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (BacktraceMap::IsValid(map) && map.flags & PROT_READ) {
+ *out_value = *reinterpret_cast<word_t*>(ptr);
+ return true;
+ } else {
+ BACK_ASYNC_SAFE_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
+ *out_value = static_cast<word_t>(-1);
+ return false;
+ }
+}
+
+size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+ bytes = MIN(map.end - addr, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(addr), bytes);
+ return bytes;
+}
+
+bool BacktraceCurrent::Unwind(size_t num_ignore_frames, void* ucontext) {
+ if (GetMap() == nullptr) {
+ // Without a map object, we can't do anything.
+ error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+ return false;
+ }
+
+ error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
+ if (ucontext) {
+ return UnwindFromContext(num_ignore_frames, ucontext);
+ }
+
+ if (Tid() != static_cast<pid_t>(android::base::GetThreadId())) {
+ return UnwindThread(num_ignore_frames);
+ }
+
+ return UnwindFromContext(num_ignore_frames, nullptr);
+}
+
+bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string library = basename(frame.map.name.c_str());
+ if (library == "libunwind.so" || library == "libbacktrace.so") {
+ return true;
+ }
+ }
+ return false;
+}
+
+static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Since errno is stored per thread, changing it in the signal handler
+// modifies the value on the thread in which the signal handler executes.
+// If a signal occurs between a call and an errno check, it's possible
+// to get the errno set here. Always save and restore it just in case
+// code would modify it.
+class ErrnoRestorer {
+ public:
+ ErrnoRestorer() : saved_errno_(errno) {}
+ ~ErrnoRestorer() {
+ errno = saved_errno_;
+ }
+
+ private:
+ int saved_errno_;
+};
+
+static void SignalLogOnly(int, siginfo_t*, void*) {
+ ErrnoRestorer restore;
+
+ BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(),
+ static_cast<int>(android::base::GetThreadId()), THREAD_SIGNAL);
+}
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+ ErrnoRestorer restore;
+
+ ThreadEntry* entry = ThreadEntry::Get(getpid(), android::base::GetThreadId(), false);
+ if (!entry) {
+ BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(),
+ static_cast<int>(android::base::GetThreadId()));
+ return;
+ }
+
+ entry->CopyUcontextFromSigcontext(sigcontext);
+
+ // Indicate the ucontext is now valid.
+ entry->Wake();
+
+ // Pause the thread until the unwind is complete. This avoids having
+ // the thread run ahead causing problems.
+ // The number indicates that we are waiting for the second Wake() call
+ // overall which is made by the thread requesting an unwind.
+ if (entry->Wait(2)) {
+ // Do not remove the entry here because that can result in a deadlock
+ // if the code cannot properly send a signal to the thread under test.
+ entry->Wake();
+ } else {
+ // At this point, it is possible that entry has been freed, so just exit.
+ BACK_ASYNC_SAFE_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+ }
+}
+
+bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
+ // Prevent multiple threads trying to set the trigger action on different
+ // threads at the same time.
+ pthread_mutex_lock(&g_sigaction_mutex);
+
+ ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
+ entry->Lock();
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
+ BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
+ return false;
+ }
+
+ if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
+ // Do not emit an error message, this might be expected. Set the
+ // error and let the caller decide.
+ if (errno == ESRCH) {
+ error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+ } else {
+ error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
+ }
+
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ ThreadEntry::Remove(entry);
+ pthread_mutex_unlock(&g_sigaction_mutex);
+ return false;
+ }
+
+ // Wait for the thread to get the ucontext. The number indicates
+ // that we are waiting for the first Wake() call made by the thread.
+ bool wait_completed = entry->Wait(1);
+
+ if (!wait_completed && oldact.sa_sigaction == nullptr) {
+ // If the wait failed, it could be that the signal could not be delivered
+ // within the timeout. Add a signal handler that's simply going to log
+ // something so that we don't crash if the signal eventually gets
+ // delivered. Only do this if there isn't already an action set up.
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLogOnly;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ sigaction(THREAD_SIGNAL, &act, nullptr);
+ } else {
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ }
+ // After the thread has received the signal, allow other unwinders to
+ // continue.
+ pthread_mutex_unlock(&g_sigaction_mutex);
+
+ bool unwind_done = false;
+ if (wait_completed) {
+ unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
+
+ // Tell the signal handler to exit and release the entry.
+ entry->Wake();
+
+ // Wait for the thread to indicate it is done with the ThreadEntry.
+ if (!entry->Wait(3)) {
+ // Send a warning, but do not mark as a failure to unwind.
+ BACK_ASYNC_SAFE_LOGW("Timed out waiting for signal handler to indicate it finished.");
+ }
+ } else {
+ // Check to see if the thread has disappeared.
+ if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
+ error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+ } else {
+ error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+ BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ }
+ }
+
+ ThreadEntry::Remove(entry);
+
+ return unwind_done;
+}
diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h
new file mode 100644
index 000000000..48c14eaf3
--- /dev/null
+++ b/libbacktrace/BacktraceCurrent.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_CURRENT_H
+#define _LIBBACKTRACE_BACKTRACE_CURRENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+// The signal used to cause a thread to dump the stack.
+#if defined(__GLIBC__)
+// In order to run the backtrace_tests on the host, we can't use
+// the internal real time signals used by GLIBC. To avoid this,
+// use SIGRTMIN for the signal to dump the stack.
+#define THREAD_SIGNAL SIGRTMIN
+#else
+#define THREAD_SIGNAL (__SIGRTMIN+1)
+#endif
+
+class BacktraceMap;
+
+class BacktraceCurrent : public Backtrace {
+ public:
+ BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktraceCurrent() {}
+
+ size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
+
+ bool ReadWord(uint64_t ptr, word_t* out_value) override;
+
+ bool Unwind(size_t num_ignore_frames, void* ucontext) override;
+
+ protected:
+ bool DiscardFrame(const backtrace_frame_data_t& frame);
+
+ private:
+ bool UnwindThread(size_t num_ignore_frames);
+
+ virtual bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) = 0;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
new file mode 100644
index 000000000..5c39f1c04
--- /dev/null
+++ b/libbacktrace/BacktraceLog.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_LOG_H
+
+#define LOG_TAG "libbacktrace"
+
+#include <log/log.h>
+
+// Macro to log the function name along with the warning message.
+#define BACK_LOGW(format, ...) \
+ ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#define BACK_LOGE(format, ...) \
+ ALOGE("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
new file mode 100644
index 000000000..781819af5
--- /dev/null
+++ b/libbacktrace/BacktraceMap.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "backtrace-map"
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <backtrace/backtrace_constants.h>
+#if defined(__linux__)
+#include <procinfo/process_map.h>
+#endif
+
+using android::base::StringPrintf;
+
+std::string backtrace_map_t::Name() const {
+ if (!name.empty()) return name;
+ if (start == 0 && end == 0) return "";
+ return StringPrintf("<anonymous:%" PRIPTR ">", start);
+}
+
+BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
+ if (pid_ < 0) {
+ pid_ = getpid();
+ }
+}
+
+BacktraceMap::~BacktraceMap() {}
+
+void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
+ ScopedBacktraceMapIteratorLock lock(this);
+ for (auto it = begin(); it != end(); ++it) {
+ const backtrace_map_t* entry = *it;
+ if (addr >= entry->start && addr < entry->end) {
+ *map = *entry;
+ return;
+ }
+ }
+ *map = {};
+}
+
+#if defined(__APPLE__)
+static bool ParseLine(const char* line, backtrace_map_t* map) {
+ uint64_t start;
+ uint64_t end;
+ char permissions[5];
+ int name_pos;
+
+ // Mac OS vmmap(1) output:
+ // __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW
+ // /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+ // 012345678901234567890123456789012345678901234567890123456789
+ // 0 1 2 3 4 5
+ if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n", &start, &end,
+ permissions, &name_pos) != 3) {
+ return false;
+ }
+
+ map->start = start;
+ map->end = end;
+ map->flags = PROT_NONE;
+ if (permissions[0] == 'r') {
+ map->flags |= PROT_READ;
+ }
+ if (permissions[1] == 'w') {
+ map->flags |= PROT_WRITE;
+ }
+ if (permissions[2] == 'x') {
+ map->flags |= PROT_EXEC;
+ }
+
+ map->name = line + name_pos;
+ if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
+ map->name.erase(map->name.length() - 1);
+ }
+
+ ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
+ reinterpret_cast<void*>(map->end), map->flags, map->name.c_str());
+ return true;
+}
+#endif // defined(__APPLE__)
+
+bool BacktraceMap::Build() {
+#if defined(__APPLE__)
+ char
+ cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+ char line[1024];
+ // cmd is guaranteed to always be big enough to hold this string.
+ snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
+ FILE* fp = popen(cmd, "r");
+ if (fp == nullptr) {
+ return false;
+ }
+
+ while (fgets(line, sizeof(line), fp)) {
+ backtrace_map_t map;
+ if (ParseLine(line, &map)) {
+ maps_.push_back(map);
+ }
+ }
+ pclose(fp);
+ return true;
+#else
+ return android::procinfo::ReadProcessMaps(
+ pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t, const char* name) {
+ maps_.resize(maps_.size() + 1);
+ backtrace_map_t& map = maps_.back();
+ map.start = start;
+ map.end = end;
+ map.flags = flags;
+ map.name = name;
+ });
+#endif
+}
+
+#if defined(__APPLE__)
+// Corkscrew and libunwind don't compile on the mac, so create a generic
+// map object.
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
+ BacktraceMap* map = new BacktraceMap(pid);
+ if (!map->Build()) {
+ delete map;
+ return nullptr;
+ }
+ return map;
+}
+#endif
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
new file mode 100644
index 000000000..9da457d3d
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include "BacktraceLog.h"
+#include "BacktracePtrace.h"
+
+#if !defined(__APPLE__)
+static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr);
+ if (*out_value == static_cast<word_t>(-1) && errno) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool BacktracePtrace::ReadWord(uint64_t ptr, word_t* out_value) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return false;
+#else
+ if (!VerifyReadWordArgs(ptr, out_value)) {
+ return false;
+ }
+
+ backtrace_map_t map;
+ FillInMap(ptr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return false;
+ }
+
+ return PtraceRead(Tid(), ptr, out_value);
+#endif
+}
+
+size_t BacktracePtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+#if defined(__APPLE__)
+ BACK_LOGW("MacOS does not support reading from another pid.");
+ return 0;
+#else
+ backtrace_map_t map;
+ FillInMap(addr, &map);
+ if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
+ return 0;
+ }
+
+ bytes = MIN(map.end - addr, bytes);
+ size_t bytes_read = 0;
+ word_t data_word;
+ size_t align_bytes = addr & (sizeof(word_t) - 1);
+ if (align_bytes != 0) {
+ if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
+ return 0;
+ }
+ size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes);
+ memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes);
+ addr += copy_bytes;
+ buffer += copy_bytes;
+ bytes -= copy_bytes;
+ bytes_read += copy_bytes;
+ }
+
+ size_t num_words = bytes / sizeof(word_t);
+ for (size_t i = 0; i < num_words; i++) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, sizeof(word_t));
+ buffer += sizeof(word_t);
+ addr += sizeof(word_t);
+ bytes_read += sizeof(word_t);
+ }
+
+ size_t left_over = bytes & (sizeof(word_t) - 1);
+ if (left_over) {
+ if (!PtraceRead(Tid(), addr, &data_word)) {
+ return bytes_read;
+ }
+ memcpy(buffer, &data_word, left_over);
+ bytes_read += left_over;
+ }
+ return bytes_read;
+#endif
+}
diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h
new file mode 100644
index 000000000..1ae3adf55
--- /dev/null
+++ b/libbacktrace/BacktracePtrace.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_PTRACE_H
+#define _LIBBACKTRACE_BACKTRACE_PTRACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/Backtrace.h>
+
+class BacktraceMap;
+
+class BacktracePtrace : public Backtrace {
+ public:
+ BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
+ virtual ~BacktracePtrace() {}
+
+ size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
+
+ bool ReadWord(uint64_t ptr, word_t* out_value) override;
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
diff --git a/libbacktrace/BacktraceTest.h b/libbacktrace/BacktraceTest.h
new file mode 100644
index 000000000..c38af0498
--- /dev/null
+++ b/libbacktrace/BacktraceTest.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_TEST_H
+#define _LIBBACKTRACE_BACKTRACE_TEST_H
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+class BacktraceTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
+
+ test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_one"));
+
+ test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_two"));
+
+ test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_three"));
+
+ test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_four"));
+
+ test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_recursive_call"));
+
+ test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
+ dlsym(dl_handle_, "test_get_context_and_wait"));
+
+ test_signal_action_ =
+ reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
+
+ test_signal_handler_ =
+ reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(dl_handle_ != nullptr);
+ ASSERT_TRUE(test_level_one_ != nullptr);
+ ASSERT_TRUE(test_level_two_ != nullptr);
+ ASSERT_TRUE(test_level_three_ != nullptr);
+ ASSERT_TRUE(test_level_four_ != nullptr);
+ ASSERT_TRUE(test_recursive_call_ != nullptr);
+ ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
+ ASSERT_TRUE(test_signal_action_ != nullptr);
+ ASSERT_TRUE(test_signal_handler_ != nullptr);
+ }
+
+ public:
+ static void* dl_handle_;
+ static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_recursive_call_)(int, void (*)(void*), void*);
+ static void (*test_get_context_and_wait_)(void*, volatile int*);
+ static void (*test_signal_action_)(int, siginfo_t*, void*);
+ static void (*test_signal_handler_)(int);
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_TEST_H
diff --git a/libbacktrace/OWNERS b/libbacktrace/OWNERS
new file mode 100644
index 000000000..bfeedca0e
--- /dev/null
+++ b/libbacktrace/OWNERS
@@ -0,0 +1,2 @@
+cferris@google.com
+jmgao@google.com
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
new file mode 100644
index 000000000..9bd59e4a2
--- /dev/null
+++ b/libbacktrace/ThreadEntry.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <ucontext.h>
+
+#include "BacktraceAsyncSafeLog.h"
+#include "ThreadEntry.h"
+
+// Initialize static member variables.
+ThreadEntry* ThreadEntry::list_ = nullptr;
+pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// creating a ThreadEntry object.
+ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
+ : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
+ wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
+ next_(ThreadEntry::list_), prev_(nullptr) {
+ pthread_condattr_t attr;
+ pthread_condattr_init(&attr);
+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+ pthread_cond_init(&wait_cond_, &attr);
+
+ // Add ourselves to the list.
+ if (ThreadEntry::list_) {
+ ThreadEntry::list_->prev_ = this;
+ }
+ ThreadEntry::list_ = this;
+}
+
+ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ ThreadEntry* entry = list_;
+ while (entry != nullptr) {
+ if (entry->Match(pid, tid)) {
+ break;
+ }
+ entry = entry->next_;
+ }
+
+ if (!entry) {
+ if (create) {
+ entry = new ThreadEntry(pid, tid);
+ }
+ } else {
+ entry->ref_count_++;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+
+ return entry;
+}
+
+void ThreadEntry::Remove(ThreadEntry* entry) {
+ entry->Unlock();
+
+ pthread_mutex_lock(&ThreadEntry::list_mutex_);
+ if (--entry->ref_count_ == 0) {
+ delete entry;
+ }
+ pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+}
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// deleting a ThreadEntry object.
+ThreadEntry::~ThreadEntry() {
+ if (list_ == this) {
+ list_ = next_;
+ } else {
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ prev_->next_ = next_;
+ }
+
+ next_ = nullptr;
+ prev_ = nullptr;
+
+ pthread_cond_destroy(&wait_cond_);
+}
+
+bool ThreadEntry::Wait(int value) {
+ timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ ts.tv_sec += 5;
+
+ bool wait_completed = true;
+ pthread_mutex_lock(&wait_mutex_);
+ while (wait_value_ != value) {
+ int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
+ if (ret != 0) {
+ BACK_ASYNC_SAFE_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+ wait_completed = false;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&wait_mutex_);
+
+ return wait_completed;
+}
+
+void ThreadEntry::Wake() {
+ pthread_mutex_lock(&wait_mutex_);
+ wait_value_++;
+ pthread_mutex_unlock(&wait_mutex_);
+
+ pthread_cond_signal(&wait_cond_);
+}
+
+void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
+ // The only thing the unwinder cares about is the mcontext data.
+ memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
+}
diff --git a/libbacktrace/ThreadEntry.h b/libbacktrace/ThreadEntry.h
new file mode 100644
index 000000000..caa549715
--- /dev/null
+++ b/libbacktrace/ThreadEntry.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_THREAD_ENTRY_H
+#define _LIBBACKTRACE_THREAD_ENTRY_H
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+class ThreadEntry {
+ public:
+ static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
+
+ static void Remove(ThreadEntry* entry);
+
+ void Wake();
+
+ bool Wait(int);
+
+ void CopyUcontextFromSigcontext(void*);
+
+ inline void Lock() {
+ pthread_mutex_lock(&mutex_);
+
+ // Always reset the wait value since this could be the first or nth
+ // time this entry is locked.
+ wait_value_ = 0;
+ }
+
+ inline void Unlock() {
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ inline ucontext_t* GetUcontext() { return &ucontext_; }
+
+ private:
+ ThreadEntry(pid_t pid, pid_t tid);
+ ~ThreadEntry();
+
+ bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); }
+
+ pid_t pid_;
+ pid_t tid_;
+ int ref_count_;
+ pthread_mutex_t mutex_;
+ pthread_mutex_t wait_mutex_;
+ pthread_cond_t wait_cond_;
+ int wait_value_;
+ ThreadEntry* next_;
+ ThreadEntry* prev_;
+ ucontext_t ucontext_;
+
+ static ThreadEntry* list_;
+ static pthread_mutex_t list_mutex_;
+};
+
+#endif // _LIBBACKTRACE_THREAD_ENTRY_H
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
new file mode 100644
index 000000000..798c769c8
--- /dev/null
+++ b/libbacktrace/UnwindMap.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <backtrace/BacktraceMap.h>
+
+#include <libunwind.h>
+
+#include "BacktraceLog.h"
+#include "UnwindMap.h"
+
+//-------------------------------------------------------------------------
+// libunwind has a single shared address space for the current process
+// aka local. If multiple maps are created for the current pid, then
+// only update the local address space once, and keep a reference count
+// of maps using the same map cursor.
+//-------------------------------------------------------------------------
+UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
+ unw_map_cursor_clear(&map_cursor_);
+}
+
+UnwindMapRemote::UnwindMapRemote(pid_t pid) : UnwindMap(pid) {
+}
+
+UnwindMapRemote::~UnwindMapRemote() {
+ unw_map_cursor_destroy(&map_cursor_);
+ unw_map_cursor_clear(&map_cursor_);
+}
+
+bool UnwindMapRemote::GenerateMap() {
+ // Use the map_cursor information to construct the BacktraceMap data
+ // rather than reparsing /proc/self/maps.
+ unw_map_cursor_reset(&map_cursor_);
+
+ unw_map_t unw_map;
+ while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
+ backtrace_map_t map;
+
+ map.start = unw_map.start;
+ map.end = unw_map.end;
+ map.offset = unw_map.offset;
+ map.load_bias = unw_map.load_base;
+ map.flags = unw_map.flags;
+ map.name = unw_map.path;
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+ }
+
+ return true;
+}
+
+bool UnwindMapRemote::Build() {
+ return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
+}
+
+UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+ pthread_rwlock_init(&map_lock_, nullptr);
+}
+
+UnwindMapLocal::~UnwindMapLocal() {
+ if (map_created_) {
+ unw_map_local_destroy();
+ unw_map_cursor_clear(&map_cursor_);
+ }
+}
+
+bool UnwindMapLocal::GenerateMap() {
+ // Lock so that multiple threads cannot modify the maps data at the
+ // same time.
+ pthread_rwlock_wrlock(&map_lock_);
+
+ // It's possible for the map to be regenerated while this loop is occurring.
+ // If that happens, get the map again, but only try at most three times
+ // before giving up.
+ bool generated = false;
+ for (int i = 0; i < 3; i++) {
+ maps_.clear();
+
+ // Save the map data retrieved so we can tell if it changes.
+ unw_map_local_cursor_get(&map_cursor_);
+
+ unw_map_t unw_map;
+ int ret;
+ while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
+ backtrace_map_t map;
+
+ map.start = unw_map.start;
+ map.end = unw_map.end;
+ map.offset = unw_map.offset;
+ map.load_bias = unw_map.load_base;
+ map.flags = unw_map.flags;
+ map.name = unw_map.path;
+
+ free(unw_map.path);
+
+ // The maps are in descending order, but we want them in ascending order.
+ maps_.push_front(map);
+ }
+ // Check to see if the map changed while getting the data.
+ if (ret != -UNW_EINVAL) {
+ generated = true;
+ break;
+ }
+ }
+
+ pthread_rwlock_unlock(&map_lock_);
+
+ if (!generated) {
+ BACK_LOGW("Unable to generate the map.");
+ }
+ return generated;
+}
+
+bool UnwindMapLocal::Build() {
+ return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
+}
+
+void UnwindMapLocal::FillIn(uint64_t addr, backtrace_map_t* map) {
+ BacktraceMap::FillIn(addr, map);
+ if (!IsValid(*map)) {
+ // Check to see if the underlying map changed and regenerate the map
+ // if it did.
+ if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
+ if (GenerateMap()) {
+ BacktraceMap::FillIn(addr, map);
+ }
+ }
+ }
+}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
new file mode 100644
index 000000000..15544e82b
--- /dev/null
+++ b/libbacktrace/UnwindMap.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_MAP_H
+#define _LIBBACKTRACE_UNWIND_MAP_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <backtrace/BacktraceMap.h>
+
+// The unw_map_cursor_t structure is different depending on whether it is
+// the local or remote version. In order to get the correct version, include
+// libunwind.h first then this header.
+
+class UnwindMap : public BacktraceMap {
+ public:
+ explicit UnwindMap(pid_t pid);
+
+ unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
+
+ protected:
+ unw_map_cursor_t map_cursor_;
+};
+
+class UnwindMapRemote : public UnwindMap {
+ public:
+ explicit UnwindMapRemote(pid_t pid);
+ virtual ~UnwindMapRemote();
+
+ bool Build() override;
+
+ private:
+ bool GenerateMap();
+};
+
+class UnwindMapLocal : public UnwindMap {
+ public:
+ UnwindMapLocal();
+ virtual ~UnwindMapLocal();
+
+ bool Build() override;
+
+ void FillIn(uint64_t addr, backtrace_map_t* map) override;
+
+ void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
+ void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
+
+ private:
+ bool GenerateMap();
+
+ bool map_created_;
+
+ pthread_rwlock_t map_lock_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
new file mode 100644
index 000000000..624711f79
--- /dev/null
+++ b/libbacktrace/UnwindStack.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
+#include <unwindstack/Unwinder.h>
+
+#include "BacktraceLog.h"
+#include "UnwindStack.h"
+#include "UnwindStackMap.h"
+
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
+bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+ std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
+ std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
+ UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+ auto process_memory = stack_map->process_memory();
+ unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+ regs, stack_map->process_memory());
+ unwinder.SetResolveNames(stack_map->ResolveNames());
+ stack_map->SetArch(regs->Arch());
+ if (stack_map->GetJitDebug() != nullptr) {
+ unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
+ }
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ if (stack_map->GetDexFiles() != nullptr) {
+ unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
+ }
+#endif
+ unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
+ if (error != nullptr) {
+ switch (unwinder.LastErrorCode()) {
+ case unwindstack::ERROR_NONE:
+ error->error_code = BACKTRACE_UNWIND_NO_ERROR;
+ break;
+
+ case unwindstack::ERROR_MEMORY_INVALID:
+ error->error_code = BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED;
+ error->error_info.addr = unwinder.LastErrorAddress();
+ break;
+
+ case unwindstack::ERROR_UNWIND_INFO:
+ error->error_code = BACKTRACE_UNWIND_ERROR_UNWIND_INFO;
+ break;
+
+ case unwindstack::ERROR_UNSUPPORTED:
+ error->error_code = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
+ break;
+
+ case unwindstack::ERROR_INVALID_MAP:
+ error->error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+ break;
+
+ case unwindstack::ERROR_MAX_FRAMES_EXCEEDED:
+ error->error_code = BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT;
+ break;
+
+ case unwindstack::ERROR_REPEATED_FRAME:
+ error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
+ break;
+
+ case unwindstack::ERROR_INVALID_ELF:
+ error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
+ break;
+ }
+ }
+
+ if (num_ignore_frames >= unwinder.NumFrames()) {
+ frames->resize(0);
+ return true;
+ }
+
+ auto unwinder_frames = unwinder.frames();
+ frames->resize(unwinder.NumFrames() - num_ignore_frames);
+ size_t cur_frame = 0;
+ for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++) {
+ auto frame = &unwinder_frames[i];
+
+ backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
+
+ back_frame->num = cur_frame++;
+
+ back_frame->rel_pc = frame->rel_pc;
+ back_frame->pc = frame->pc;
+ back_frame->sp = frame->sp;
+
+ char* demangled_name = __cxa_demangle(frame->function_name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name != nullptr) {
+ back_frame->func_name = demangled_name;
+ free(demangled_name);
+ } else {
+ back_frame->func_name = frame->function_name;
+ }
+ back_frame->func_offset = frame->function_offset;
+
+ back_frame->map.name = frame->map_name;
+ back_frame->map.start = frame->map_start;
+ back_frame->map.end = frame->map_end;
+ back_frame->map.offset = frame->map_elf_start_offset;
+ back_frame->map.load_bias = frame->map_load_bias;
+ back_frame->map.flags = frame->map_flags;
+ }
+
+ return true;
+}
+
+UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktraceCurrent(pid, tid, map) {}
+
+std::string UnwindStackCurrent::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
+ return GetMap()->GetFunctionName(pc, offset);
+}
+
+bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, void* ucontext) {
+ std::unique_ptr<unwindstack::Regs> regs;
+ if (ucontext == nullptr) {
+ regs.reset(unwindstack::Regs::CreateFromLocal());
+ // Fill in the registers from this function. Do it here to avoid
+ // one extra function call appearing in the unwind.
+ unwindstack::RegsGetLocal(regs.get());
+ } else {
+ regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
+ }
+
+ std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
+ if (!skip_frames_) {
+ skip_names.clear();
+ }
+ return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names, &error_);
+}
+
+UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+ : BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
+
+std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
+ return GetMap()->GetFunctionName(pc, offset);
+}
+
+bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, void* context) {
+ std::unique_ptr<unwindstack::Regs> regs;
+ if (context == nullptr) {
+ regs.reset(unwindstack::Regs::RemoteGet(Tid()));
+ } else {
+ regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
+ }
+
+ return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
+}
+
+size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+ return memory_->Read(addr, buffer, bytes);
+}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
new file mode 100644
index 000000000..47f6757d8
--- /dev/null
+++ b/libbacktrace/UnwindStack.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_STACK_H
+#define _LIBBACKTRACE_UNWIND_STACK_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktracePtrace.h"
+
+class UnwindStackCurrent : public BacktraceCurrent {
+ public:
+ UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindStackCurrent() = default;
+
+ std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
+
+ bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) override;
+};
+
+class UnwindStackPtrace : public BacktracePtrace {
+ public:
+ UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+ virtual ~UnwindStackPtrace() = default;
+
+ bool Unwind(size_t num_ignore_frames, void* context) override;
+
+ std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
+
+ size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
+
+ private:
+ std::shared_ptr<unwindstack::Memory> memory_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
new file mode 100644
index 000000000..aa0b17c99
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+
+#include "UnwindStackMap.h"
+
+//-------------------------------------------------------------------------
+UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
+
+bool UnwindStackMap::Build() {
+ if (pid_ == 0) {
+ pid_ = getpid();
+ stack_maps_.reset(new unwindstack::LocalMaps);
+ } else {
+ stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
+ }
+
+ // Create the process memory object.
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
+
+ // Create a JitDebug object for getting jit unwind information.
+ std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
+ jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
+#endif
+
+ if (!stack_maps_->Parse()) {
+ return false;
+ }
+
+ // Iterate through the maps and fill in the backtrace_map_t structure.
+ for (const auto& map_info : *stack_maps_) {
+ backtrace_map_t map;
+ map.start = map_info->start;
+ map.end = map_info->end;
+ map.offset = map_info->offset;
+ // Set to -1 so that it is demand loaded.
+ map.load_bias = static_cast<uint64_t>(-1);
+ map.flags = map_info->flags;
+ map.name = map_info->name;
+
+ maps_.push_back(map);
+ }
+
+ return true;
+}
+
+void UnwindStackMap::FillIn(uint64_t addr, backtrace_map_t* map) {
+ BacktraceMap::FillIn(addr, map);
+ if (map->load_bias != static_cast<uint64_t>(-1)) {
+ return;
+ }
+
+ // Fill in the load_bias.
+ unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+ if (map_info == nullptr) {
+ return;
+ }
+ map->load_bias = map_info->GetLoadBias(process_memory_);
+}
+
+uint64_t UnwindStackMap::GetLoadBias(size_t index) {
+ if (index >= stack_maps_->Total()) {
+ return 0;
+ }
+
+ unwindstack::MapInfo* map_info = stack_maps_->Get(index);
+ if (map_info == nullptr) {
+ return 0;
+ }
+ return map_info->GetLoadBias(process_memory_);
+}
+
+std::string UnwindStackMap::GetFunctionName(uint64_t pc, uint64_t* offset) {
+ *offset = 0;
+ unwindstack::Maps* maps = stack_maps();
+
+ // Get the map for this
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
+ return "";
+ }
+
+ if (arch_ == unwindstack::ARCH_UNKNOWN) {
+ if (pid_ == getpid()) {
+ arch_ = unwindstack::Regs::CurrentArch();
+ } else {
+ // Create a remote regs, to figure out the architecture.
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid_));
+ arch_ = regs->Arch();
+ }
+ }
+
+ unwindstack::Elf* elf = map_info->GetElf(process_memory(), arch_);
+
+ std::string name;
+ uint64_t func_offset;
+ if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+ return "";
+ }
+ *offset = func_offset;
+ return name;
+}
+
+std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
+ return process_memory_;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
+ BacktraceMap* map;
+
+ if (uncached) {
+ // Force use of the base class to parse the maps when this call is made.
+ map = new BacktraceMap(pid);
+ } else if (pid == getpid()) {
+ map = new UnwindStackMap(0);
+ } else {
+ map = new UnwindStackMap(pid);
+ }
+ if (!map->Build()) {
+ delete map;
+ return nullptr;
+ }
+ return map;
+}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
new file mode 100644
index 000000000..f0e7d8b78
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWINDSTACK_MAP_H
+#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+// Forward declarations.
+class UnwindDexFile;
+
+class UnwindStackMap : public BacktraceMap {
+ public:
+ explicit UnwindStackMap(pid_t pid);
+ ~UnwindStackMap() = default;
+
+ bool Build() override;
+
+ void FillIn(uint64_t addr, backtrace_map_t* map) override;
+
+ virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset) override;
+ virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() override final;
+
+ unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
+
+ const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
+
+ unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
+#endif
+
+ void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
+
+ protected:
+ uint64_t GetLoadBias(size_t index) override;
+
+ std::unique_ptr<unwindstack::Maps> stack_maps_;
+ std::shared_ptr<unwindstack::Memory> process_memory_;
+ std::unique_ptr<unwindstack::JitDebug> jit_debug_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ std::unique_ptr<unwindstack::DexFiles> dex_files_;
+#endif
+
+ unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
+};
+
+#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
new file mode 100644
index 000000000..a93a25e90
--- /dev/null
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/threads.h>
+
+#include <benchmark/benchmark.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+constexpr size_t kNumMaps = 2000;
+
+static bool CountMaps(pid_t pid, size_t* num_maps) {
+ // Minimize the calls that might allocate memory. If too much memory
+ // gets allocated, then this routine will add extra maps and the next
+ // call will fail to get the same number of maps as before.
+ int fd =
+ open((std::string("/proc/") + std::to_string(pid) + "/maps").c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ fprintf(stderr, "Cannot open map file for pid %d: %s\n", pid, strerror(errno));
+ return false;
+ }
+ *num_maps = 0;
+ while (true) {
+ char buffer[2048];
+ ssize_t bytes = read(fd, buffer, sizeof(buffer));
+ if (bytes <= 0) {
+ break;
+ }
+ // Count the '\n'.
+ for (size_t i = 0; i < static_cast<size_t>(bytes); i++) {
+ if (buffer[i] == '\n') {
+ ++*num_maps;
+ }
+ }
+ }
+
+ close(fd);
+ return true;
+}
+
+static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
+ // Create a remote process so that the map data is exactly the same.
+ // Also, so that we can create a set number of maps.
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ size_t num_maps;
+ if (!CountMaps(getpid(), &num_maps)) {
+ exit(1);
+ }
+ // Create uniquely named maps.
+ std::vector<void*> maps;
+ for (size_t i = num_maps; i < kNumMaps; i++) {
+ int flags = PROT_READ | PROT_WRITE;
+ // Alternate page type to make sure a map entry is added for each call.
+ if ((i % 2) == 0) {
+ flags |= PROT_EXEC;
+ }
+ void* memory = mmap(nullptr, PAGE_SIZE, flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (memory == MAP_FAILED) {
+ fprintf(stderr, "Failed to create map: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(memory, 0x1, PAGE_SIZE);
+#if defined(PR_SET_VMA)
+ if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") == -1) {
+ fprintf(stderr, "Failed: %s\n", strerror(errno));
+ }
+#endif
+ maps.push_back(memory);
+ }
+
+ if (!CountMaps(getpid(), &num_maps)) {
+ exit(1);
+ }
+
+ if (num_maps < kNumMaps) {
+ fprintf(stderr, "Maps set incorrectly: %zu found, %zu expected at least.\n", num_maps,
+ kNumMaps);
+ std::string str;
+ android::base::ReadFileToString("/proc/self/maps", &str);
+ fprintf(stderr, "%s\n", str.c_str());
+ exit(1);
+ }
+
+ // Wait for an hour at most.
+ sleep(3600);
+ exit(1);
+ } else if (pid < 0) {
+ fprintf(stderr, "Fork failed: %s\n", strerror(errno));
+ return;
+ }
+
+ size_t num_maps = 0;
+ for (size_t i = 0; i < 2000; i++) {
+ if (CountMaps(pid, &num_maps) && num_maps >= kNumMaps) {
+ break;
+ }
+ usleep(1000);
+ }
+ if (num_maps < kNumMaps) {
+ fprintf(stderr, "Timed out waiting for the number of maps available: %zu\n", num_maps);
+ return;
+ }
+
+ while (state.KeepRunning()) {
+ BacktraceMap* map = map_func(pid, false);
+ if (map == nullptr) {
+ fprintf(stderr, "Failed to create map\n");
+ return;
+ }
+ delete map;
+ }
+
+ kill(pid, SIGKILL);
+ waitpid(pid, nullptr, 0);
+}
+
+static void BM_create_map(benchmark::State& state) {
+ CreateMap(state, BacktraceMap::Create);
+}
+BENCHMARK(BM_create_map);
+
+using BacktraceCreateFn = decltype(Backtrace::Create);
+
+static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
+ while (state.KeepRunning()) {
+ std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
+ backtrace->Unwind(0);
+ }
+}
+
+static void BM_create_backtrace(benchmark::State& state) {
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid()));
+ CreateBacktrace(state, backtrace_map.get(), Backtrace::Create);
+}
+BENCHMARK(BM_create_backtrace);
+
+BENCHMARK_MAIN();
diff --git a/libbacktrace/backtrace_read_benchmarks.cpp b/libbacktrace/backtrace_read_benchmarks.cpp
new file mode 100644
index 000000000..6a688b0c2
--- /dev/null
+++ b/libbacktrace/backtrace_read_benchmarks.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+#include <backtrace/Backtrace.h>
+
+#define AT_COMMON_SIZES Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(100)->Arg(200)->Arg(500)->Arg(1024)
+
+static void Attach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ perror("Failed to attach");
+ abort();
+ }
+
+ siginfo_t si;
+ // Wait for up to 5 seconds.
+ for (size_t i = 0; i < 5000; i++) {
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return;
+ }
+ usleep(1000);
+ }
+ printf("Remote process failed to stop in five seconds.\n");
+ abort();
+}
+
+class ScopedPidReaper {
+ public:
+ ScopedPidReaper(pid_t pid) : pid_(pid) {}
+ ~ScopedPidReaper() {
+ kill(pid_, SIGKILL);
+ waitpid(pid_, nullptr, 0);
+ }
+
+ private:
+ pid_t pid_;
+};
+
+static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
+ struct iovec dst_iov = {
+ .iov_base = dst, .iov_len = len,
+ };
+
+ struct iovec src_iov = {
+ .iov_base = reinterpret_cast<void*>(remote_src), .iov_len = len,
+ };
+
+ ssize_t rc = process_vm_readv(pid, &dst_iov, 1, &src_iov, 1, 0);
+ return rc == -1 ? 0 : rc;
+}
+
+static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+ if (*value == -1 && errno) {
+ return false;
+ }
+ return true;
+}
+
+static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
+ size_t bytes_read = 0;
+ long data;
+ for (size_t i = 0; i < bytes / sizeof(long); i++) {
+ if (!PtraceReadLong(pid, addr, &data)) {
+ return bytes_read;
+ }
+ memcpy(dst, &data, sizeof(long));
+ dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
+ addr += sizeof(long);
+ bytes_read += sizeof(long);
+ }
+
+ size_t left_over = bytes & (sizeof(long) - 1);
+ if (left_over) {
+ if (!PtraceReadLong(pid, addr, &data)) {
+ return bytes_read;
+ }
+ memcpy(dst, &data, left_over);
+ bytes_read += left_over;
+ }
+ return bytes_read;
+}
+
+static void CreateRemoteProcess(size_t size, void** map, pid_t* pid) {
+ *map = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (*map == MAP_FAILED) {
+ perror("Can't allocate memory");
+ abort();
+ }
+ memset(*map, 0xaa, size);
+
+ if ((*pid = fork()) == 0) {
+ for (volatile int i = 0;; i++)
+ ;
+ exit(1);
+ }
+ if (*pid < 0) {
+ perror("Failed to fork");
+ abort();
+ }
+ Attach(*pid);
+ // Don't need this map in the current process any more.
+ munmap(*map, size);
+}
+
+static void BM_read_with_ptrace(benchmark::State& state) {
+ void* map;
+ pid_t pid;
+ CreateRemoteProcess(state.range(0), &map, &pid);
+ ScopedPidReaper reap(pid);
+
+ std::vector<uint8_t> read_buffer(state.range(0));
+ uint64_t addr = reinterpret_cast<uint64_t>(map);
+ while (state.KeepRunning()) {
+ if (PtraceRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
+ printf("Unexpected bad read.\n");
+ abort();
+ }
+ }
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+BENCHMARK(BM_read_with_ptrace)->AT_COMMON_SIZES;
+
+static void BM_read_with_process_vm_read(benchmark::State& state) {
+ void* map;
+ pid_t pid;
+ CreateRemoteProcess(state.range(0), &map, &pid);
+ ScopedPidReaper reap(pid);
+
+ std::vector<uint8_t> read_buffer(state.range(0));
+ uint64_t addr = reinterpret_cast<uint64_t>(map);
+ while (state.KeepRunning()) {
+ if (ProcessVmRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
+ printf("Unexpected bad read.\n");
+ abort();
+ }
+ }
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+BENCHMARK(BM_read_with_process_vm_read)->AT_COMMON_SIZES;
+
+static void BM_read_with_backtrace_object(benchmark::State& state) {
+ void* map;
+ pid_t pid;
+ CreateRemoteProcess(state.range(0), &map, &pid);
+ ScopedPidReaper reap(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ if (backtrace.get() == nullptr) {
+ printf("Failed to create backtrace.\n");
+ abort();
+ }
+
+ uint64_t addr = reinterpret_cast<uint64_t>(map);
+ std::vector<uint8_t> read_buffer(state.range(0));
+ while (state.KeepRunning()) {
+ if (backtrace->Read(addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
+ printf("Unexpected bad read.\n");
+ abort();
+ }
+ }
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+BENCHMARK(BM_read_with_backtrace_object)->AT_COMMON_SIZES;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
new file mode 100644
index 000000000..f4191b908
--- /dev/null
+++ b/libbacktrace/backtrace_test.cpp
@@ -0,0 +1,1893 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <list>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <android-base/threads.h>
+#include <android-base/unique_fd.h>
+#include <cutils/atomic.h>
+
+#include <gtest/gtest.h>
+
+// For the THREAD_SIGNAL definition.
+#include "BacktraceCurrent.h"
+#include "BacktraceTest.h"
+#include "backtrace_testlib.h"
+
+// Number of microseconds per milliseconds.
+#define US_PER_MSEC 1000
+
+// Number of nanoseconds in a second.
+#define NS_PER_SEC 1000000000ULL
+
+// Number of simultaneous dumping operations to perform.
+#define NUM_THREADS 40
+
+// Number of simultaneous threads running in our forked process.
+#define NUM_PTRACE_THREADS 5
+
+// The list of shared libaries that make up the backtrace library.
+static std::vector<std::string> kBacktraceLibs{"libunwindstack.so", "libbacktrace.so"};
+
+struct thread_t {
+ pid_t tid;
+ int32_t state;
+ pthread_t threadId;
+ void* data;
+};
+
+struct dump_thread_t {
+ thread_t thread;
+ BacktraceMap* map;
+ Backtrace* backtrace;
+ int32_t* now;
+ int32_t done;
+};
+
+typedef Backtrace* (*create_func_t)(pid_t, pid_t, BacktraceMap*);
+typedef BacktraceMap* (*map_create_func_t)(pid_t, bool);
+
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+ map_create_func_t map_func = nullptr);
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+ map_create_func_t map_func = nullptr);
+
+void* BacktraceTest::dl_handle_;
+int (*BacktraceTest::test_level_one_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_two_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_three_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_four_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_recursive_call_)(int, void (*)(void*), void*);
+void (*BacktraceTest::test_get_context_and_wait_)(void*, volatile int*);
+void (*BacktraceTest::test_signal_action_)(int, siginfo_t*, void*);
+void (*BacktraceTest::test_signal_handler_)(int);
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+ static const char* initial_args[] = {"--slow_threshold_ms=8000", "--deadline_threshold_ms=15000"};
+ *args = initial_args;
+ *num_args = 2;
+ return true;
+}
+
+static uint64_t NanoTime() {
+ struct timespec t = { 0, 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
+}
+
+static std::string DumpFrames(Backtrace* backtrace) {
+ if (backtrace->NumFrames() == 0) {
+ return " No frames to dump.\n";
+ }
+
+ std::string frame;
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ frame += " " + backtrace->FormatFrameData(i) + '\n';
+ }
+ return frame;
+}
+
+static void WaitForStop(pid_t pid) {
+ uint64_t start = NanoTime();
+
+ siginfo_t si;
+ while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
+ if ((NanoTime() - start) > NS_PER_SEC) {
+ printf("The process did not get to a stopping point in 1 second.\n");
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+}
+
+static void CreateRemoteProcess(pid_t* pid) {
+ if ((*pid = fork()) == 0) {
+ while (true)
+ ;
+ _exit(0);
+ }
+ ASSERT_NE(-1, *pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, *pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(*pid);
+}
+
+static void FinishRemoteProcess(pid_t pid) {
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+}
+
+#if !defined(__ANDROID__) || defined(__arm__)
+// On host and arm target we aren't guaranteed that we will terminate cleanly.
+#define VERIFY_NO_ERROR(error_code) \
+ ASSERT_TRUE(error_code == BACKTRACE_UNWIND_NO_ERROR || \
+ error_code == BACKTRACE_UNWIND_ERROR_UNWIND_INFO || \
+ error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING) \
+ << "Unknown error code " << std::to_string(error_code);
+#else
+#define VERIFY_NO_ERROR(error_code) ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, error_code);
+#endif
+
+static bool ReadyLevelBacktrace(Backtrace* backtrace) {
+ // See if test_level_four is in the backtrace.
+ bool found = false;
+ for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) {
+ if (it->func_name == "test_level_four") {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
+ ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
+ << DumpFrames(backtrace);
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
+
+ // Look through the frames starting at the highest to find the
+ // frame we want.
+ size_t frame_num = 0;
+ for (size_t i = backtrace->NumFrames()-1; i > 2; i--) {
+ if (backtrace->GetFrame(i)->func_name == "test_level_one") {
+ frame_num = i;
+ break;
+ }
+ }
+ ASSERT_LT(static_cast<size_t>(0), frame_num) << DumpFrames(backtrace);
+ ASSERT_LE(static_cast<size_t>(3), frame_num) << DumpFrames(backtrace);
+
+ ASSERT_EQ(backtrace->GetFrame(frame_num)->func_name, "test_level_one")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-1)->func_name, "test_level_two")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-2)->func_name, "test_level_three")
+ << DumpFrames(backtrace);
+ ASSERT_EQ(backtrace->GetFrame(frame_num-3)->func_name, "test_level_four")
+ << DumpFrames(backtrace);
+}
+
+static void VerifyLevelBacktrace(void*) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ VerifyLevelDump(backtrace.get());
+}
+
+static bool ReadyMaxBacktrace(Backtrace* backtrace) {
+ return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
+}
+
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
+ ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
+ // Verify that the last frame is our recursive call.
+ ASSERT_EQ(backtrace->GetFrame(MAX_BACKTRACE_FRAMES-1)->func_name, "test_recursive_call")
+ << DumpFrames(backtrace);
+}
+
+static void VerifyMaxBacktrace(void*) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
+
+ VerifyMaxDump(backtrace.get());
+}
+
+static void ThreadSetState(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+ android_atomic_acquire_store(1, &thread->state);
+ volatile int i = 0;
+ while (thread->state) {
+ i++;
+ }
+}
+
+static bool WaitForNonZero(int32_t* value, uint64_t seconds) {
+ uint64_t start = NanoTime();
+ do {
+ if (android_atomic_acquire_load(value)) {
+ return true;
+ }
+ } while ((NanoTime() - start) < seconds * NS_PER_SEC);
+ return false;
+}
+
+TEST_F(BacktraceTest, local_no_unwind_frames) {
+ // Verify that a local unwind does not include any frames within
+ // libunwind or libbacktrace.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ ASSERT_TRUE(backtrace->NumFrames() != 0);
+ // None of the frames should be in the backtrace libraries.
+ for (const auto& frame : *backtrace ) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string name = basename(frame.map.name.c_str());
+ for (const auto& lib : kBacktraceLibs) {
+ ASSERT_TRUE(name != lib) << DumpFrames(backtrace.get());
+ }
+ }
+ }
+}
+
+TEST_F(BacktraceTest, local_unwind_frames) {
+ // Verify that a local unwind with the skip frames disabled does include
+ // frames within the backtrace libraries.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ backtrace->SetSkipFrames(false);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ ASSERT_TRUE(backtrace->NumFrames() != 0);
+ size_t first_frame_non_backtrace_lib = 0;
+ for (const auto& frame : *backtrace) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string name = basename(frame.map.name.c_str());
+ bool found = false;
+ for (const auto& lib : kBacktraceLibs) {
+ if (name == lib) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ first_frame_non_backtrace_lib = frame.num;
+ break;
+ }
+ }
+ }
+
+ ASSERT_NE(0U, first_frame_non_backtrace_lib) << "No frames found in backtrace libraries:\n"
+ << DumpFrames(backtrace.get());
+}
+
+TEST_F(BacktraceTest, local_trace) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+}
+
+static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
+ const char* cur_proc) {
+ ASSERT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1) << "All backtrace:\n"
+ << DumpFrames(bt_all)
+ << "Ignore 1 backtrace:\n"
+ << DumpFrames(bt_ign1);
+ ASSERT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2) << "All backtrace:\n"
+ << DumpFrames(bt_all)
+ << "Ignore 2 backtrace:\n"
+ << DumpFrames(bt_ign2);
+
+ // Check all of the frames are the same > the current frame.
+ bool check = (cur_proc == nullptr);
+ for (size_t i = 0; i < bt_ign2->NumFrames(); i++) {
+ if (check) {
+ EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_ign1->GetFrame(i+1)->pc);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_ign1->GetFrame(i+1)->sp);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_ign1->GetFrame(i+1)->stack_size);
+
+ EXPECT_EQ(bt_ign2->GetFrame(i)->pc, bt_all->GetFrame(i+2)->pc);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->sp, bt_all->GetFrame(i+2)->sp);
+ EXPECT_EQ(bt_ign2->GetFrame(i)->stack_size, bt_all->GetFrame(i+2)->stack_size);
+ }
+ if (!check && bt_ign2->GetFrame(i)->func_name == cur_proc) {
+ check = true;
+ }
+ }
+}
+
+static void VerifyLevelIgnoreFrames(void*) {
+ std::unique_ptr<Backtrace> all(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(all.get() != nullptr);
+ ASSERT_TRUE(all->Unwind(0));
+ VERIFY_NO_ERROR(all->GetError().error_code);
+
+ std::unique_ptr<Backtrace> ign1(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign1.get() != nullptr);
+ ASSERT_TRUE(ign1->Unwind(1));
+ VERIFY_NO_ERROR(ign1->GetError().error_code);
+
+ std::unique_ptr<Backtrace> ign2(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(ign2.get() != nullptr);
+ ASSERT_TRUE(ign2->Unwind(2));
+ VERIFY_NO_ERROR(ign2->GetError().error_code);
+
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
+}
+
+TEST_F(BacktraceTest, local_trace_ignore_frames) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+}
+
+TEST_F(BacktraceTest, local_max_trace) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxBacktrace, nullptr), 0);
+}
+
+static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
+ void (*VerifyFunc)(Backtrace*, create_func_t, map_create_func_t),
+ create_func_t create_func, map_create_func_t map_create_func) {
+ pid_t ptrace_tid;
+ if (tid < 0) {
+ ptrace_tid = pid;
+ } else {
+ ptrace_tid = tid;
+ }
+ uint64_t start = NanoTime();
+ bool verified = false;
+ std::string last_dump;
+ do {
+ usleep(US_PER_MSEC);
+ if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) {
+ // Wait for the process to get to a stopping point.
+ WaitForStop(ptrace_tid);
+
+ std::unique_ptr<BacktraceMap> map;
+ map.reset(map_create_func(pid, false));
+ std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ if (ReadyFunc(backtrace.get())) {
+ VerifyFunc(backtrace.get(), create_func, map_create_func);
+ verified = true;
+ } else {
+ last_dump = DumpFrames(backtrace.get());
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0);
+ }
+ // If 5 seconds have passed, then we are done.
+ } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC);
+ ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
+}
+
+TEST_F(BacktraceTest, ptrace_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
+ Backtrace::Create, BacktraceMap::Create);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST_F(BacktraceTest, ptrace_max_trace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
+ BacktraceMap::Create);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_func,
+ map_create_func_t map_create_func) {
+ std::unique_ptr<BacktraceMap> map(map_create_func(bt_all->Pid(), false));
+ std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
+ ASSERT_TRUE(ign1.get() != nullptr);
+ ASSERT_TRUE(ign1->Unwind(1));
+ VERIFY_NO_ERROR(ign1->GetError().error_code);
+
+ std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
+ ASSERT_TRUE(ign2.get() != nullptr);
+ ASSERT_TRUE(ign2->Unwind(2));
+ VERIFY_NO_ERROR(ign2->GetError().error_code);
+
+ VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
+}
+
+TEST_F(BacktraceTest, ptrace_ignore_frames) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+ VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
+ Backtrace::Create, BacktraceMap::Create);
+
+ kill(pid, SIGKILL);
+ int status;
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+// Create a process with multiple threads and dump all of the threads.
+static void* PtraceThreadLevelRun(void*) {
+ EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
+ return nullptr;
+}
+
+static void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
+ // Get the list of tasks.
+ char task_path[128];
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+ std::unique_ptr<DIR, decltype(&closedir)> tasks_dir(opendir(task_path), closedir);
+ ASSERT_TRUE(tasks_dir != nullptr);
+ struct dirent* entry;
+ while ((entry = readdir(tasks_dir.get())) != nullptr) {
+ char* end;
+ pid_t tid = strtoul(entry->d_name, &end, 10);
+ if (*end == '\0') {
+ threads->push_back(tid);
+ }
+ }
+}
+
+TEST_F(BacktraceTest, ptrace_threads) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
+ }
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
+ _exit(1);
+ }
+
+ // Check to see that all of the threads are running before unwinding.
+ std::vector<pid_t> threads;
+ uint64_t start = NanoTime();
+ do {
+ usleep(US_PER_MSEC);
+ threads.clear();
+ GetThreads(pid, &threads);
+ } while ((threads.size() != NUM_PTRACE_THREADS + 1) &&
+ ((NanoTime() - start) <= 5 * NS_PER_SEC));
+ ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+ WaitForStop(pid);
+ for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
+ // Skip the current forked process, we only care about the threads.
+ if (pid == *it) {
+ continue;
+ }
+ VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::Create,
+ BacktraceMap::Create);
+ }
+
+ FinishRemoteProcess(pid);
+}
+
+void VerifyLevelThread(void*) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ VerifyLevelDump(backtrace.get());
+}
+
+TEST_F(BacktraceTest, thread_current_level) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+}
+
+static void VerifyMaxThread(void*) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
+
+ VerifyMaxDump(backtrace.get());
+}
+
+TEST_F(BacktraceTest, thread_current_max) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxThread, nullptr), 0);
+}
+
+static void* ThreadLevelRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = android::base::GetThreadId();
+ EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, ThreadSetState, data), 0);
+ return nullptr;
+}
+
+TEST_F(BacktraceTest, thread_level_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ // Make sure that the thread signal used is not visible when compiled for
+ // the target.
+#if !defined(__GLIBC__)
+ ASSERT_LT(THREAD_SIGNAL, SIGRTMIN);
+#endif
+
+ // Save the current signal action and make sure it is restored afterwards.
+ struct sigaction cur_action;
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &cur_action) == 0);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ VerifyLevelDump(backtrace.get());
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ // Verify that the old action was restored.
+ struct sigaction new_action;
+ ASSERT_TRUE(sigaction(THREAD_SIGNAL, nullptr, &new_action) == 0);
+ EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
+ // The SA_RESTORER flag gets set behind our back, so a direct comparison
+ // doesn't work unless we mask the value off. Mips doesn't have this
+ // flag, so skip this on that platform.
+#if defined(SA_RESTORER)
+ cur_action.sa_flags &= ~SA_RESTORER;
+ new_action.sa_flags &= ~SA_RESTORER;
+#elif defined(__GLIBC__)
+ // Our host compiler doesn't appear to define this flag for some reason.
+ cur_action.sa_flags &= ~0x04000000;
+ new_action.sa_flags &= ~0x04000000;
+#endif
+ EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
+}
+
+TEST_F(BacktraceTest, thread_ignore_frames) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(all.get() != nullptr);
+ ASSERT_TRUE(all->Unwind(0));
+ VERIFY_NO_ERROR(all->GetError().error_code);
+
+ std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign1.get() != nullptr);
+ ASSERT_TRUE(ign1->Unwind(1));
+ VERIFY_NO_ERROR(ign1->GetError().error_code);
+
+ std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(ign2.get() != nullptr);
+ ASSERT_TRUE(ign2->Unwind(2));
+ VERIFY_NO_ERROR(ign2->GetError().error_code);
+
+ VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+static void* ThreadMaxRun(void* data) {
+ thread_t* thread = reinterpret_cast<thread_t*>(data);
+
+ thread->tid = android::base::GetThreadId();
+ EXPECT_NE(BacktraceTest::test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, ThreadSetState, data),
+ 0);
+ return nullptr;
+}
+
+TEST_F(BacktraceTest, thread_max_trace) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0);
+
+ // Wait for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
+
+ VerifyMaxDump(backtrace.get());
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+}
+
+static void* ThreadDump(void* data) {
+ dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data);
+ while (true) {
+ if (android_atomic_acquire_load(dump->now)) {
+ break;
+ }
+ }
+
+ // The status of the actual unwind will be checked elsewhere.
+ dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid, dump->map);
+ dump->backtrace->Unwind(0);
+
+ android_atomic_acquire_store(1, &dump->done);
+
+ return nullptr;
+}
+
+static void MultipleThreadDumpTest(bool share_map) {
+ // Dump NUM_THREADS simultaneously using the same map.
+ std::vector<thread_t> runners(NUM_THREADS);
+ std::vector<dump_thread_t> dumpers(NUM_THREADS);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ // Launch the runners, they will spin in hard loops doing nothing.
+ runners[i].tid = 0;
+ runners[i].state = 0;
+ ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0);
+ }
+
+ // Wait for tids to be set.
+ for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) {
+ ASSERT_TRUE(WaitForNonZero(&it->state, 30));
+ }
+
+ // Start all of the dumpers at once, they will spin until they are signalled
+ // to begin their dump run.
+ std::unique_ptr<BacktraceMap> map;
+ if (share_map) {
+ map.reset(BacktraceMap::Create(getpid()));
+ }
+ int32_t dump_now = 0;
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ dumpers[i].thread.tid = runners[i].tid;
+ dumpers[i].thread.state = 0;
+ dumpers[i].done = 0;
+ dumpers[i].now = &dump_now;
+ dumpers[i].map = map.get();
+
+ ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+ }
+
+ // Start all of the dumpers going at once.
+ android_atomic_acquire_store(1, &dump_now);
+
+ for (size_t i = 0; i < NUM_THREADS; i++) {
+ ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
+
+ // Tell the runner thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &runners[i].state);
+
+ ASSERT_TRUE(dumpers[i].backtrace != nullptr);
+ VerifyMaxDump(dumpers[i].backtrace);
+
+ delete dumpers[i].backtrace;
+ dumpers[i].backtrace = nullptr;
+ }
+}
+
+TEST_F(BacktraceTest, thread_multiple_dump) {
+ MultipleThreadDumpTest(false);
+}
+
+TEST_F(BacktraceTest, thread_multiple_dump_same_map) {
+ MultipleThreadDumpTest(true);
+}
+
+// This test is for UnwindMaps that should share the same map cursor when
+// multiple maps are created for the current process at the same time.
+TEST_F(BacktraceTest, simultaneous_maps) {
+ BacktraceMap* map1 = BacktraceMap::Create(getpid());
+ BacktraceMap* map2 = BacktraceMap::Create(getpid());
+ BacktraceMap* map3 = BacktraceMap::Create(getpid());
+
+ Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
+ ASSERT_TRUE(back1 != nullptr);
+ EXPECT_TRUE(back1->Unwind(0));
+ VERIFY_NO_ERROR(back1->GetError().error_code);
+ delete back1;
+ delete map1;
+
+ Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
+ ASSERT_TRUE(back2 != nullptr);
+ EXPECT_TRUE(back2->Unwind(0));
+ VERIFY_NO_ERROR(back2->GetError().error_code);
+ delete back2;
+ delete map2;
+
+ Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
+ ASSERT_TRUE(back3 != nullptr);
+ EXPECT_TRUE(back3->Unwind(0));
+ VERIFY_NO_ERROR(back3->GetError().error_code);
+ delete back3;
+ delete map3;
+}
+
+TEST_F(BacktraceTest, fillin_erases) {
+ BacktraceMap* back_map = BacktraceMap::Create(getpid());
+
+ backtrace_map_t map;
+
+ map.start = 1;
+ map.end = 3;
+ map.flags = 1;
+ map.name = "Initialized";
+ back_map->FillIn(0, &map);
+ delete back_map;
+
+ ASSERT_FALSE(BacktraceMap::IsValid(map));
+ ASSERT_EQ(static_cast<uint64_t>(0), map.start);
+ ASSERT_EQ(static_cast<uint64_t>(0), map.end);
+ ASSERT_EQ(0, map.flags);
+ ASSERT_EQ("", map.name);
+}
+
+TEST_F(BacktraceTest, format_test) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ backtrace_frame_data_t frame;
+ frame.num = 1;
+ frame.pc = 2;
+ frame.rel_pc = 2;
+ frame.sp = 0;
+ frame.stack_size = 0;
+ frame.func_offset = 0;
+
+ // Check no map set.
+ frame.num = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000002 <unknown>",
+#else
+ EXPECT_EQ("#01 pc 00000002 <unknown>",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check map name empty, but exists.
+ frame.pc = 0xb0020;
+ frame.rel_pc = 0x20;
+ frame.map.start = 0xb0000;
+ frame.map.end = 0xbffff;
+ frame.map.load_bias = 0;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000020 <anonymous:00000000000b0000>",
+#else
+ EXPECT_EQ("#01 pc 00000020 <anonymous:000b0000>",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check map name begins with a [.
+ frame.pc = 0xc0020;
+ frame.map.start = 0xc0000;
+ frame.map.end = 0xcffff;
+ frame.map.load_bias = 0;
+ frame.map.name = "[anon:thread signal stack]";
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000000000020 [anon:thread signal stack:00000000000c0000]",
+#else
+ EXPECT_EQ("#01 pc 00000020 [anon:thread signal stack:000c0000]",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check relative pc is set and map name is set.
+ frame.pc = 0x12345679;
+ frame.rel_pc = 0x12345678;
+ frame.map.name = "MapFake";
+ frame.map.start = 1;
+ frame.map.end = 1;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, but no func offset.
+ frame.func_name = "ProcFake";
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake)",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake)",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, and func offset is non-zero.
+ frame.func_offset = 645;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 0000000012345678 MapFake (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 12345678 MapFake (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check func_name is set, func offset is non-zero, and load_bias is non-zero.
+ frame.rel_pc = 0x123456dc;
+ frame.func_offset = 645;
+ frame.map.load_bias = 100;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 00000000123456dc MapFake (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 123456dc MapFake (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
+
+ // Check a non-zero map offset.
+ frame.map.offset = 0x1000;
+#if defined(__LP64__)
+ EXPECT_EQ("#01 pc 00000000123456dc MapFake (offset 0x1000) (ProcFake+645)",
+#else
+ EXPECT_EQ("#01 pc 123456dc MapFake (offset 0x1000) (ProcFake+645)",
+#endif
+ backtrace->FormatFrameData(&frame));
+}
+
+struct map_test_t {
+ uint64_t start;
+ uint64_t end;
+};
+
+static bool map_sort(map_test_t i, map_test_t j) { return i.start < j.start; }
+
+static std::string GetTestMapsAsString(const std::vector<map_test_t>& maps) {
+ if (maps.size() == 0) {
+ return "No test map entries\n";
+ }
+ std::string map_txt;
+ for (auto map : maps) {
+ map_txt += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 "\n", map.start, map.end);
+ }
+ return map_txt;
+}
+
+static std::string GetMapsAsString(BacktraceMap* maps) {
+ if (maps->size() == 0) {
+ return "No map entries\n";
+ }
+ std::string map_txt;
+ for (const backtrace_map_t* map : *maps) {
+ map_txt += android::base::StringPrintf(
+ "%" PRIx64 "-%" PRIx64 " flags: 0x%x offset: 0x%" PRIx64 " load_bias: 0x%" PRIx64,
+ map->start, map->end, map->flags, map->offset, map->load_bias);
+ if (!map->name.empty()) {
+ map_txt += ' ' + map->name;
+ }
+ map_txt += '\n';
+ }
+ return map_txt;
+}
+
+static void VerifyMap(pid_t pid) {
+ char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+
+ FILE* map_file = fopen(buffer, "r");
+ ASSERT_TRUE(map_file != nullptr);
+ std::vector<map_test_t> test_maps;
+ while (fgets(buffer, sizeof(buffer), map_file)) {
+ map_test_t map;
+ ASSERT_EQ(2, sscanf(buffer, "%" SCNx64 "-%" SCNx64 " ", &map.start, &map.end));
+ test_maps.push_back(map);
+ }
+ fclose(map_file);
+ std::sort(test_maps.begin(), test_maps.end(), map_sort);
+
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+ // Basic test that verifies that the map is in the expected order.
+ auto test_it = test_maps.begin();
+ for (auto it = map->begin(); it != map->end(); ++it) {
+ ASSERT_TRUE(test_it != test_maps.end()) << "Mismatch in number of maps, expected test maps:\n"
+ << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+ << GetMapsAsString(map.get());
+ ASSERT_EQ(test_it->start, (*it)->start) << "Mismatch in map data, expected test maps:\n"
+ << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+ << GetMapsAsString(map.get());
+ ASSERT_EQ(test_it->end, (*it)->end) << "Mismatch maps in map data, expected test maps:\n"
+ << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+ << GetMapsAsString(map.get());
+ // Make sure the load bias get set to a value.
+ ASSERT_NE(static_cast<uint64_t>(-1), (*it)->load_bias) << "Found uninitialized load_bias\n"
+ << GetMapsAsString(map.get());
+ ++test_it;
+ }
+ ASSERT_TRUE(test_it == test_maps.end());
+}
+
+TEST_F(BacktraceTest, verify_map_remote) {
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ // The maps should match exactly since the forked process has been paused.
+ VerifyMap(pid);
+
+ FinishRemoteProcess(pid);
+}
+
+static void InitMemory(uint8_t* memory, size_t bytes) {
+ for (size_t i = 0; i < bytes; i++) {
+ memory[i] = i;
+ if (memory[i] == '\0') {
+ // Don't use '\0' in our data so we can verify that an overread doesn't
+ // occur by using a '\0' as the character after the read data.
+ memory[i] = 23;
+ }
+ }
+}
+
+static void* ThreadReadTest(void* data) {
+ thread_t* thread_data = reinterpret_cast<thread_t*>(data);
+
+ thread_data->tid = android::base::GetThreadId();
+
+ // Create two map pages.
+ // Mark the second page as not-readable.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+
+ if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+
+ // Set up a simple pattern in memory.
+ InitMemory(memory, pagesize);
+
+ thread_data->data = memory;
+
+ // Tell the caller it's okay to start reading memory.
+ android_atomic_acquire_store(1, &thread_data->state);
+
+ // Loop waiting for the caller to finish reading the memory.
+ while (thread_data->state) {
+ }
+
+ // Re-enable read-write on the page so that we don't crash if we try
+ // and access data on this page when freeing the memory.
+ if (mprotect(&memory[pagesize], pagesize, PROT_READ | PROT_WRITE) != 0) {
+ return reinterpret_cast<void*>(-1);
+ }
+ free(memory);
+
+ android_atomic_acquire_store(1, &thread_data->state);
+
+ return nullptr;
+}
+
+static void RunReadTest(Backtrace* backtrace, uint64_t read_addr) {
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+
+ // Create a page of data to use to do quick compares.
+ uint8_t* expected = new uint8_t[pagesize];
+ InitMemory(expected, pagesize);
+
+ uint8_t* data = new uint8_t[2 * pagesize];
+ // Verify that we can only read one page worth of data.
+ size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize);
+ ASSERT_EQ(pagesize, bytes_read);
+ ASSERT_TRUE(memcmp(data, expected, pagesize) == 0);
+
+ // Verify unaligned reads.
+ for (size_t i = 1; i < sizeof(word_t); i++) {
+ bytes_read = backtrace->Read(read_addr + i, data, 2 * sizeof(word_t));
+ ASSERT_EQ(2 * sizeof(word_t), bytes_read);
+ ASSERT_TRUE(memcmp(data, &expected[i], 2 * sizeof(word_t)) == 0)
+ << "Offset at " << i << " failed";
+ }
+
+ // Verify small unaligned reads.
+ for (size_t i = 1; i < sizeof(word_t); i++) {
+ for (size_t j = 1; j < sizeof(word_t); j++) {
+ // Set one byte past what we expect to read, to guarantee we don't overread.
+ data[j] = '\0';
+ bytes_read = backtrace->Read(read_addr + i, data, j);
+ ASSERT_EQ(j, bytes_read);
+ ASSERT_TRUE(memcmp(data, &expected[i], j) == 0)
+ << "Offset at " << i << " length " << j << " miscompared";
+ ASSERT_EQ('\0', data[j])
+ << "Offset at " << i << " length " << j << " wrote too much data";
+ }
+ }
+ delete[] data;
+ delete[] expected;
+}
+
+TEST_F(BacktraceTest, thread_read) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_t thread;
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ ASSERT_TRUE(pthread_create(&thread, &attr, ThreadReadTest, &thread_data) == 0);
+
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ RunReadTest(backtrace.get(), reinterpret_cast<uint64_t>(thread_data.data));
+
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
+}
+
+// The code requires these variables are the same size.
+volatile uint64_t g_ready = 0;
+volatile uint64_t g_addr = 0;
+static_assert(sizeof(g_ready) == sizeof(g_addr), "g_ready/g_addr must be same size");
+
+static void ForkedReadTest() {
+ // Create two map pages.
+ size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
+ uint8_t* memory;
+ if (posix_memalign(reinterpret_cast<void**>(&memory), pagesize, 2 * pagesize) != 0) {
+ perror("Failed to allocate memory\n");
+ exit(1);
+ }
+
+ // Mark the second page as not-readable.
+ if (mprotect(&memory[pagesize], pagesize, PROT_NONE) != 0) {
+ perror("Failed to mprotect memory\n");
+ exit(1);
+ }
+
+ // Set up a simple pattern in memory.
+ InitMemory(memory, pagesize);
+
+ g_addr = reinterpret_cast<uint64_t>(memory);
+ g_ready = 1;
+
+ while (1) {
+ usleep(US_PER_MSEC);
+ }
+}
+
+TEST_F(BacktraceTest, process_read) {
+ g_ready = 0;
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ ForkedReadTest();
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+
+ bool test_executed = false;
+ uint64_t start = NanoTime();
+ while (1) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ uint64_t read_addr;
+ size_t bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(&g_ready),
+ reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_ready));
+ ASSERT_EQ(sizeof(g_ready), bytes_read);
+ if (read_addr) {
+ // The forked process is ready to be read.
+ bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(&g_addr),
+ reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_addr));
+ ASSERT_EQ(sizeof(g_addr), bytes_read);
+
+ RunReadTest(backtrace.get(), read_addr);
+
+ test_executed = true;
+ break;
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+ }
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ ASSERT_TRUE(test_executed);
+}
+
+static void VerifyFunctionsFound(const std::vector<std::string>& found_functions) {
+ // We expect to find these functions in libbacktrace_test. If we don't
+ // find them, that's a bug in the memory read handling code in libunwind.
+ std::list<std::string> expected_functions;
+ expected_functions.push_back("test_recursive_call");
+ expected_functions.push_back("test_level_one");
+ expected_functions.push_back("test_level_two");
+ expected_functions.push_back("test_level_three");
+ expected_functions.push_back("test_level_four");
+ for (const auto& found_function : found_functions) {
+ for (const auto& expected_function : expected_functions) {
+ if (found_function == expected_function) {
+ expected_functions.remove(found_function);
+ break;
+ }
+ }
+ }
+ ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
+}
+
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string test_lib(testing::internal::GetArgvs()[0]);
+ auto const value = test_lib.find_last_of('/');
+ if (value == std::string::npos) {
+ test_lib = "../backtrace_test_libs/";
+ } else {
+ test_lib = test_lib.substr(0, value + 1) + "../backtrace_test_libs/";
+ }
+ test_lib += "libbacktrace_test.so";
+
+ *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+ std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
+
+ // Copy the shared so to a tempory directory.
+ ASSERT_EQ(0, system(cp_cmd.c_str()));
+}
+
+TEST_F(BacktraceTest, check_unreadable_elf_local) {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ struct stat buf;
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
+ uint64_t map_size = buf.st_size;
+
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
+ ASSERT_TRUE(map != MAP_FAILED);
+ close(fd);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
+
+ std::vector<std::string> found_functions;
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
+ BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ // Needed before GetFunctionName will work.
+ backtrace->Unwind(0);
+
+ // Loop through the entire map, and get every function we can find.
+ map_size += reinterpret_cast<uint64_t>(map);
+ std::string last_func;
+ for (uint64_t read_addr = reinterpret_cast<uint64_t>(map); read_addr < map_size; read_addr += 4) {
+ uint64_t offset;
+ std::string func_name = backtrace->GetFunctionName(read_addr, &offset);
+ if (!func_name.empty() && last_func != func_name) {
+ found_functions.push_back(func_name);
+ }
+ last_func = func_name;
+ }
+
+ ASSERT_TRUE(munmap(map, map_size - reinterpret_cast<uint64_t>(map)) == 0);
+
+ VerifyFunctionsFound(found_functions);
+}
+
+TEST_F(BacktraceTest, check_unreadable_elf_remote) {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ g_ready = 0;
+
+ struct stat buf;
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
+ uint64_t map_size = buf.st_size;
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+ unlink(tmp_so_name.c_str());
+ exit(0);
+ }
+
+ void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
+ unlink(tmp_so_name.c_str());
+ exit(0);
+ }
+ close(fd);
+ if (unlink(tmp_so_name.c_str()) == -1) {
+ fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
+ exit(0);
+ }
+
+ g_addr = reinterpret_cast<uint64_t>(map);
+ g_ready = 1;
+ while (true) {
+ usleep(US_PER_MSEC);
+ }
+ exit(0);
+ }
+ ASSERT_TRUE(pid > 0);
+
+ std::vector<std::string> found_functions;
+ uint64_t start = NanoTime();
+ while (true) {
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ uint64_t read_addr;
+ ASSERT_EQ(sizeof(g_ready),
+ backtrace->Read(reinterpret_cast<uint64_t>(&g_ready),
+ reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_ready)));
+ if (read_addr) {
+ ASSERT_EQ(sizeof(g_addr),
+ backtrace->Read(reinterpret_cast<uint64_t>(&g_addr),
+ reinterpret_cast<uint8_t*>(&read_addr), sizeof(uint64_t)));
+
+ // Needed before GetFunctionName will work.
+ backtrace->Unwind(0);
+
+ // Loop through the entire map, and get every function we can find.
+ map_size += read_addr;
+ std::string last_func;
+ for (; read_addr < map_size; read_addr += 4) {
+ uint64_t offset;
+ std::string func_name = backtrace->GetFunctionName(read_addr, &offset);
+ if (!func_name.empty() && last_func != func_name) {
+ found_functions.push_back(func_name);
+ }
+ last_func = func_name;
+ }
+ break;
+ }
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ VerifyFunctionsFound(found_functions);
+}
+
+static bool FindFuncFrameInBacktrace(Backtrace* backtrace, uint64_t test_func, size_t* frame_num) {
+ backtrace_map_t map;
+ backtrace->FillInMap(test_func, &map);
+ if (!BacktraceMap::IsValid(map)) {
+ return false;
+ }
+
+ // Loop through the frames, and find the one that is in the map.
+ *frame_num = 0;
+ for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map) && map.start == it->map.start &&
+ it->pc >= test_func) {
+ *frame_num = it->num;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void VerifyUnreadableElfFrame(Backtrace* backtrace, uint64_t test_func, size_t frame_num) {
+ ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
+ << DumpFrames(backtrace);
+
+ ASSERT_TRUE(frame_num != 0) << DumpFrames(backtrace);
+ // Make sure that there is at least one more frame above the test func call.
+ ASSERT_LT(frame_num, backtrace->NumFrames()) << DumpFrames(backtrace);
+
+ uint64_t diff = backtrace->GetFrame(frame_num)->pc - test_func;
+ ASSERT_LT(diff, 200U) << DumpFrames(backtrace);
+}
+
+static void VerifyUnreadableElfBacktrace(void* func) {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
+ BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ size_t frame_num;
+ uint64_t test_func = reinterpret_cast<uint64_t>(func);
+ ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num))
+ << DumpFrames(backtrace.get());
+
+ VerifyUnreadableElfFrame(backtrace.get(), test_func, frame_num);
+}
+
+typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
+
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_local) {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
+ ASSERT_TRUE(lib_handle != nullptr);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
+
+ test_func_t test_func;
+ test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
+ ASSERT_TRUE(test_func != nullptr);
+
+ ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, reinterpret_cast<void*>(test_func)),
+ 0);
+}
+
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_remote) {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
+ ASSERT_TRUE(lib_handle != nullptr);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
+
+ test_func_t test_func;
+ test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
+ ASSERT_TRUE(test_func != nullptr);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ test_func(1, 2, 3, 4, 0, 0);
+ exit(0);
+ }
+ ASSERT_TRUE(pid > 0);
+
+ uint64_t start = NanoTime();
+ bool done = false;
+ while (!done) {
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ size_t frame_num;
+ if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
+ &frame_num) &&
+ frame_num != 0) {
+ VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
+ done = true;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ if ((NanoTime() - start) > 5 * NS_PER_SEC) {
+ break;
+ }
+ usleep(US_PER_MSEC);
+ }
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+
+ ASSERT_TRUE(done) << "Test function never found in unwind.";
+}
+
+TEST_F(BacktraceTest, unwind_thread_doesnt_exist) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_FALSE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
+}
+
+TEST_F(BacktraceTest, local_get_function_name_before_unwind) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ // Verify that trying to get a function name before doing an unwind works.
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
+ uint64_t offset;
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
+}
+
+TEST_F(BacktraceTest, remote_get_function_name_before_unwind) {
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ // Verify that trying to get a function name before doing an unwind works.
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
+ uint64_t offset;
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
+
+ FinishRemoteProcess(pid);
+}
+
+static void SetUcontextSp(uint64_t sp, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_sp = sp;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.sp = sp;
+#elif defined(__i386__)
+ ucontext->uc_mcontext.gregs[REG_ESP] = sp;
+#elif defined(__x86_64__)
+ ucontext->uc_mcontext.gregs[REG_RSP] = sp;
+#else
+ UNUSED(sp);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static void SetUcontextPc(uint64_t pc, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_pc = pc;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.pc = pc;
+#elif defined(__i386__)
+ ucontext->uc_mcontext.gregs[REG_EIP] = pc;
+#elif defined(__x86_64__)
+ ucontext->uc_mcontext.gregs[REG_RIP] = pc;
+#else
+ UNUSED(pc);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static void SetUcontextLr(uint64_t lr, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_lr = lr;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.regs[30] = lr;
+#elif defined(__i386__)
+ // The lr is on the stack.
+ ASSERT_TRUE(lr != 0);
+ ASSERT_TRUE(ucontext != nullptr);
+#elif defined(__x86_64__)
+ // The lr is on the stack.
+ ASSERT_TRUE(lr != 0);
+ ASSERT_TRUE(ucontext != nullptr);
+#else
+ UNUSED(lr);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static constexpr size_t DEVICE_MAP_SIZE = 1024;
+
+static void SetupDeviceMap(void** device_map) {
+ // Make sure that anything in a device map will result in fails
+ // to read.
+ android::base::unique_fd device_fd(open("/dev/zero", O_RDONLY | O_CLOEXEC));
+
+ *device_map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE, device_fd, 0);
+ ASSERT_TRUE(*device_map != MAP_FAILED);
+
+ // Make sure the map is readable.
+ ASSERT_EQ(0, reinterpret_cast<int*>(*device_map)[0]);
+}
+
+static void UnwindFromDevice(Backtrace* backtrace, void* device_map) {
+ uint64_t device_map_uint = reinterpret_cast<uint64_t>(device_map);
+
+ backtrace_map_t map;
+ backtrace->FillInMap(device_map_uint, &map);
+ // Verify the flag is set.
+ ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
+
+ // Quick sanity checks.
+ uint64_t offset;
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
+
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(BacktraceTest::test_level_one_) + 1;
+ // Now verify the device map flag actually causes the function name to be empty.
+ backtrace->FillInMap(cur_func_offset, &map);
+ ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset, &map));
+ map.flags |= PROT_DEVICE_MAP;
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset, &map));
+
+ ucontext_t ucontext;
+
+ // Create a context that has the pc in the device map, but the sp
+ // in a non-device map.
+ memset(&ucontext, 0, sizeof(ucontext));
+ SetUcontextSp(reinterpret_cast<uint64_t>(&ucontext), &ucontext);
+ SetUcontextPc(device_map_uint, &ucontext);
+ SetUcontextLr(cur_func_offset, &ucontext);
+
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The buffer should only be a single element.
+ ASSERT_EQ(1U, backtrace->NumFrames());
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(0);
+ ASSERT_EQ(device_map_uint, frame->pc);
+ ASSERT_EQ(reinterpret_cast<uint64_t>(&ucontext), frame->sp);
+
+ // Check what happens when skipping the first frame.
+ ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
+ ASSERT_EQ(0U, backtrace->NumFrames());
+
+ // Create a context that has the sp in the device map, but the pc
+ // in a non-device map.
+ memset(&ucontext, 0, sizeof(ucontext));
+ SetUcontextSp(device_map_uint, &ucontext);
+ SetUcontextPc(cur_func_offset, &ucontext);
+ SetUcontextLr(cur_func_offset, &ucontext);
+
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The buffer should only be a single element.
+ ASSERT_EQ(1U, backtrace->NumFrames());
+ frame = backtrace->GetFrame(0);
+ ASSERT_EQ(cur_func_offset, frame->pc);
+ ASSERT_EQ(device_map_uint, frame->sp);
+
+ // Check what happens when skipping the first frame.
+ ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
+ ASSERT_EQ(0U, backtrace->NumFrames());
+}
+
+TEST_F(BacktraceTest, unwind_disallow_device_map_local) {
+ void* device_map;
+ SetupDeviceMap(&device_map);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace);
+
+ UnwindFromDevice(backtrace.get(), device_map);
+
+ munmap(device_map, DEVICE_MAP_SIZE);
+}
+
+TEST_F(BacktraceTest, unwind_disallow_device_map_remote) {
+ void* device_map;
+ SetupDeviceMap(&device_map);
+
+ // Fork a process to do a remote backtrace.
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ UnwindFromDevice(backtrace.get(), device_map);
+
+ FinishRemoteProcess(pid);
+
+ munmap(device_map, DEVICE_MAP_SIZE);
+}
+
+class ScopedSignalHandler {
+ public:
+ ScopedSignalHandler(int signal_number, void (*handler)(int)) : signal_number_(signal_number) {
+ memset(&action_, 0, sizeof(action_));
+ action_.sa_handler = handler;
+ sigaction(signal_number_, &action_, &old_action_);
+ }
+
+ ScopedSignalHandler(int signal_number, void (*action)(int, siginfo_t*, void*))
+ : signal_number_(signal_number) {
+ memset(&action_, 0, sizeof(action_));
+ action_.sa_flags = SA_SIGINFO;
+ action_.sa_sigaction = action;
+ sigaction(signal_number_, &action_, &old_action_);
+ }
+
+ ~ScopedSignalHandler() { sigaction(signal_number_, &old_action_, nullptr); }
+
+ private:
+ struct sigaction action_;
+ struct sigaction old_action_;
+ const int signal_number_;
+};
+
+static void SetValueAndLoop(void* data) {
+ volatile int* value = reinterpret_cast<volatile int*>(data);
+
+ *value = 1;
+ for (volatile int i = 0;; i++)
+ ;
+}
+
+static void UnwindThroughSignal(bool use_action, create_func_t create_func,
+ map_create_func_t map_create_func) {
+ volatile int value = 0;
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ if (use_action) {
+ ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_action_);
+
+ BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ } else {
+ ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_handler_);
+
+ BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ }
+ }
+ ASSERT_NE(-1, pid);
+
+ int read_value = 0;
+ uint64_t start = NanoTime();
+ while (read_value == 0) {
+ usleep(1000);
+
+ // Loop until the remote function gets into the final function.
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ WaitForStop(pid);
+
+ std::unique_ptr<BacktraceMap> map(map_create_func(pid, false));
+ std::unique_ptr<Backtrace> backtrace(create_func(pid, pid, map.get()));
+
+ size_t bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(const_cast<int*>(&value)),
+ reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
+ ASSERT_EQ(sizeof(read_value), bytes_read);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
+ << "Remote process did not execute far enough in 5 seconds.";
+ }
+
+ // Now need to send a signal to the remote process.
+ kill(pid, SIGUSR1);
+
+ // Wait for the process to get to the signal handler loop.
+ Backtrace::const_iterator frame_iter;
+ start = NanoTime();
+ std::unique_ptr<BacktraceMap> map;
+ std::unique_ptr<Backtrace> backtrace;
+ while (true) {
+ usleep(1000);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ WaitForStop(pid);
+
+ map.reset(map_create_func(pid, false));
+ ASSERT_TRUE(map.get() != nullptr);
+ backtrace.reset(create_func(pid, pid, map.get()));
+ ASSERT_TRUE(backtrace->Unwind(0));
+ bool found = false;
+ for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
+ if (frame_iter->func_name == "test_loop_forever") {
+ ++frame_iter;
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
+ << "Remote process did not get in signal handler in 5 seconds." << std::endl
+ << DumpFrames(backtrace.get());
+ }
+
+ std::vector<std::string> names;
+ // Loop through the frames, and save the function names.
+ size_t frame = 0;
+ for (; frame_iter != backtrace->end(); ++frame_iter) {
+ if (frame_iter->func_name == "test_level_four") {
+ frame = names.size() + 1;
+ }
+ names.push_back(frame_iter->func_name);
+ }
+ ASSERT_NE(0U, frame) << "Unable to find test_level_four in backtrace" << std::endl
+ << DumpFrames(backtrace.get());
+
+ // The expected order of the frames:
+ // test_loop_forever
+ // test_signal_handler|test_signal_action
+ // <OPTIONAL_FRAME> May or may not exist.
+ // SetValueAndLoop (but the function name might be empty)
+ // test_level_four
+ // test_level_three
+ // test_level_two
+ // test_level_one
+ ASSERT_LE(frame + 2, names.size()) << DumpFrames(backtrace.get());
+ ASSERT_LE(2U, frame) << DumpFrames(backtrace.get());
+ if (use_action) {
+ ASSERT_EQ("test_signal_action", names[0]) << DumpFrames(backtrace.get());
+ } else {
+ ASSERT_EQ("test_signal_handler", names[0]) << DumpFrames(backtrace.get());
+ }
+ ASSERT_EQ("test_level_three", names[frame]) << DumpFrames(backtrace.get());
+ ASSERT_EQ("test_level_two", names[frame + 1]) << DumpFrames(backtrace.get());
+ ASSERT_EQ("test_level_one", names[frame + 2]) << DumpFrames(backtrace.get());
+
+ FinishRemoteProcess(pid);
+}
+
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_handler) {
+ UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
+}
+
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_action) {
+ UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
+}
+
+static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
+ std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
+ std::unique_ptr<Backtrace> backtrace(
+ create_func(getpid(), android::base::GetThreadId(), map.get()));
+ backtrace->Unwind(1);
+ ASSERT_NE(0U, backtrace->NumFrames());
+ ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
+}
+
+TEST_F(BacktraceTest, unwind_frame_skip_numbering) {
+ TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
+}
+
+#define MAX_LEAK_BYTES (32*1024UL)
+
+static void CheckForLeak(pid_t pid, pid_t tid) {
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+ // Loop enough that even a small leak should be detectable.
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < 4096; i++) {
+ Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
+ ASSERT_TRUE(backtrace != nullptr);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+ delete backtrace;
+
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (first_allocated_bytes == 0) {
+ first_allocated_bytes = allocated_bytes;
+ } else if (last_allocated_bytes > first_allocated_bytes) {
+ // Check that the memory did not increase too much over the first loop.
+ ASSERT_LE(last_allocated_bytes - first_allocated_bytes, MAX_LEAK_BYTES);
+ }
+ last_allocated_bytes = allocated_bytes;
+ }
+}
+
+TEST_F(BacktraceTest, check_for_leak_local) {
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
+}
+
+TEST_F(BacktraceTest, check_for_leak_local_thread) {
+ thread_t thread_data = { 0, 0, 0, nullptr };
+ pthread_t thread;
+ ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
+
+ // Wait up to 2 seconds for the tid to be set.
+ ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+ CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid);
+
+ // Tell the thread to exit its infinite loop.
+ android_atomic_acquire_store(0, &thread_data.state);
+
+ ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
+}
+
+TEST_F(BacktraceTest, check_for_leak_remote) {
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
+
+ FinishRemoteProcess(pid);
+}
diff --git a/libbacktrace/backtrace_testlib.cpp b/libbacktrace/backtrace_testlib.cpp
new file mode 100644
index 000000000..fec7d985b
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#include "backtrace_testlib.h"
+
+void test_loop_forever() {
+ while (1)
+ ;
+}
+
+void test_signal_handler(int) { test_loop_forever(); }
+
+void test_signal_action(int, siginfo_t*, void*) { test_loop_forever(); }
+
+int test_level_four(int one, int two, int three, int four, void (*callback_func)(void*),
+ void* data) {
+ if (callback_func != NULL) {
+ callback_func(data);
+ } else {
+ while (1)
+ ;
+ }
+ return one + two + three + four;
+}
+
+int test_level_three(int one, int two, int three, int four, void (*callback_func)(void*),
+ void* data) {
+ return test_level_four(one + 3, two + 6, three + 9, four + 12, callback_func, data) + 3;
+}
+
+int test_level_two(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
+ return test_level_three(one + 2, two + 4, three + 6, four + 8, callback_func, data) + 2;
+}
+
+int test_level_one(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
+ return test_level_two(one + 1, two + 2, three + 3, four + 4, callback_func, data) + 1;
+}
+
+int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
+ if (level > 0) {
+ return test_recursive_call(level - 1, callback_func, data) + level;
+ } else if (callback_func != NULL) {
+ callback_func(data);
+ } else {
+ while (1) {
+ }
+ }
+ return 0;
+}
+
+typedef struct {
+ std::vector<uint8_t>* ucontext;
+ volatile int* exit_flag;
+} GetContextArg;
+
+static void GetContextAndExit(void* data) {
+ GetContextArg* arg = reinterpret_cast<GetContextArg*>(data);
+
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+
+ ucontext_t ucontext;
+ memset(&ucontext, 0, sizeof(ucontext));
+#if defined(__arm__)
+ memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint32_t) * 16);
+#elif defined(__aarch64__)
+ memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint64_t) * 33);
+#elif defined(__i386__)
+ uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
+ ucontext.uc_mcontext.gregs[0] = reg_data[15];
+ ucontext.uc_mcontext.gregs[1] = reg_data[14];
+ ucontext.uc_mcontext.gregs[2] = reg_data[13];
+ ucontext.uc_mcontext.gregs[3] = reg_data[12];
+ ucontext.uc_mcontext.gregs[4] = reg_data[7];
+ ucontext.uc_mcontext.gregs[5] = reg_data[6];
+ ucontext.uc_mcontext.gregs[6] = reg_data[5];
+ ucontext.uc_mcontext.gregs[7] = reg_data[4];
+ ucontext.uc_mcontext.gregs[8] = reg_data[3];
+ ucontext.uc_mcontext.gregs[9] = reg_data[2];
+ ucontext.uc_mcontext.gregs[10] = reg_data[1];
+ ucontext.uc_mcontext.gregs[11] = reg_data[0];
+ ucontext.uc_mcontext.gregs[14] = reg_data[8];
+ ucontext.uc_mcontext.gregs[15] = reg_data[10];
+#elif defined(__x86_64__)
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ ucontext.uc_mcontext.gregs[0] = reg_data[8];
+ ucontext.uc_mcontext.gregs[1] = reg_data[9];
+ ucontext.uc_mcontext.gregs[2] = reg_data[10];
+ ucontext.uc_mcontext.gregs[3] = reg_data[11];
+ ucontext.uc_mcontext.gregs[4] = reg_data[12];
+ ucontext.uc_mcontext.gregs[5] = reg_data[13];
+ ucontext.uc_mcontext.gregs[6] = reg_data[14];
+ ucontext.uc_mcontext.gregs[7] = reg_data[15];
+ ucontext.uc_mcontext.gregs[8] = reg_data[5];
+ ucontext.uc_mcontext.gregs[9] = reg_data[4];
+ ucontext.uc_mcontext.gregs[10] = reg_data[6];
+ ucontext.uc_mcontext.gregs[11] = reg_data[3];
+ ucontext.uc_mcontext.gregs[12] = reg_data[1];
+ ucontext.uc_mcontext.gregs[13] = reg_data[0];
+ ucontext.uc_mcontext.gregs[14] = reg_data[2];
+ ucontext.uc_mcontext.gregs[15] = reg_data[7];
+ ucontext.uc_mcontext.gregs[16] = reg_data[16];
+#endif
+
+ arg->ucontext->resize(sizeof(ucontext));
+ memcpy(arg->ucontext->data(), &ucontext, sizeof(ucontext));
+
+ // Don't touch the stack anymore.
+ while (*arg->exit_flag == 0) {
+ }
+}
+
+void test_get_context_and_wait(void* ucontext, volatile int* exit_flag) {
+ GetContextArg arg;
+ arg.ucontext = reinterpret_cast<std::vector<uint8_t>*>(ucontext);
+ arg.exit_flag = exit_flag;
+ test_level_one(1, 2, 3, 4, GetContextAndExit, &arg);
+}
diff --git a/libbacktrace/backtrace_testlib.h b/libbacktrace/backtrace_testlib.h
new file mode 100644
index 000000000..9b55e56d4
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_TESTLIB_H
+#define _LIBBACKTRACE_BACKTRACE_TESTLIB_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void test_loop_forever();
+void test_signal_handler(int);
+void test_signal_action(int, siginfo_t*, void*);
+int test_level_four(int, int, int, int, void (*)(void*), void*);
+int test_level_three(int, int, int, int, void (*)(void*), void*);
+int test_level_two(int, int, int, int, void (*)(void*), void*);
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+int test_recursive_call(int, void (*)(void*), void*);
+void test_get_context_and_wait(void*, volatile int*);
+
+__END_DECLS
+
+#endif // _LIBBACKTRACE_BACKTRACE_TESTLIB_H
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
new file mode 100644
index 000000000..664b531fb
--- /dev/null
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKTRACE_BACKTRACE_H
+#define _BACKTRACE_BACKTRACE_H
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/backtrace_constants.h>
+#include <backtrace/BacktraceMap.h>
+
+#if defined(__LP64__)
+#define PRIPTR "016" PRIx64
+typedef uint64_t word_t;
+#else
+#define PRIPTR "08" PRIx64
+typedef uint32_t word_t;
+#endif
+
+enum BacktraceUnwindErrorCode : uint32_t {
+ BACKTRACE_UNWIND_NO_ERROR,
+ // Something failed while trying to perform the setup to begin the unwind.
+ BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
+ // There is no map information to use with the unwind.
+ BACKTRACE_UNWIND_ERROR_MAP_MISSING,
+ // An error occurred that indicates a programming error.
+ BACKTRACE_UNWIND_ERROR_INTERNAL,
+ // The thread to unwind has disappeared before the unwind can begin.
+ BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST,
+ // The thread to unwind has not responded to a signal in a timely manner.
+ BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT,
+ // Attempt to do an unsupported operation.
+ BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
+ // Attempt to do an offline unwind without a context.
+ BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+ // The count of frames exceed MAX_BACKTRACE_FRAMES.
+ BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT,
+ // Failed to read memory.
+ BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED,
+ // Failed to read registers.
+ BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED,
+ // Failed to find a function in debug sections.
+ BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED,
+ // Failed to execute dwarf instructions in debug sections.
+ BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED,
+ // Unwind information is incorrect.
+ BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
+ // Unwind information stopped due to sp/pc repeating.
+ BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
+ // Unwind information stopped due to invalid elf.
+ BACKTRACE_UNWIND_ERROR_INVALID_ELF,
+};
+
+struct BacktraceUnwindError {
+ enum BacktraceUnwindErrorCode error_code;
+
+ union {
+ // for BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED
+ uint64_t addr;
+ // for BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED
+ uint64_t regno;
+ } error_info;
+
+ BacktraceUnwindError() : error_code(BACKTRACE_UNWIND_NO_ERROR) {}
+};
+
+struct backtrace_frame_data_t {
+ size_t num; // The current fame number.
+ uint64_t pc; // The absolute pc.
+ uint64_t rel_pc; // The relative pc.
+ uint64_t sp; // The top of the stack.
+ size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
+ backtrace_map_t map; // The map associated with the given pc.
+ std::string func_name; // The function name associated with this pc, NULL if not found.
+ uint64_t func_offset; // pc relative to the start of the function, only valid if func_name is not
+ // NULL.
+};
+
+struct backtrace_stackinfo_t {
+ uint64_t start;
+ uint64_t end;
+ const uint8_t* data;
+};
+
+namespace unwindstack {
+class Regs;
+}
+
+class Backtrace {
+ public:
+ enum ArchEnum : uint8_t {
+ ARCH_ARM,
+ ARCH_ARM64,
+ ARCH_X86,
+ ARCH_X86_64,
+ };
+
+ static void SetGlobalElfCache(bool enable);
+
+ // Create the correct Backtrace object based on what is to be unwound.
+ // If pid < 0 or equals the current pid, then the Backtrace object
+ // corresponds to the current process.
+ // If pid < 0 or equals the current pid and tid >= 0, then the Backtrace
+ // object corresponds to a thread in the current process.
+ // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
+ // different process.
+ // Tracing a thread in a different process is not supported.
+ // If map is NULL, then create the map and manage it internally.
+ // If map is not NULL, the map is still owned by the caller.
+ static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
+
+ virtual ~Backtrace();
+
+ // Get the current stack trace and store in the backtrace_ structure.
+ virtual bool Unwind(size_t num_ignore_frames, void* context = nullptr) = 0;
+
+ static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+ std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
+ std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
+
+ // Get the function name and offset into the function given the pc.
+ // If the string is empty, then no valid function name was found,
+ // or the pc is not in any valid map.
+ virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset,
+ const backtrace_map_t* map = nullptr);
+
+ // Fill in the map data associated with the given pc.
+ virtual void FillInMap(uint64_t pc, backtrace_map_t* map);
+
+ // Read the data at a specific address.
+ virtual bool ReadWord(uint64_t ptr, word_t* out_value) = 0;
+
+ // Read arbitrary data from a specific address. If a read request would
+ // span from one map to another, this call only reads up until the end
+ // of the current map.
+ // Returns the total number of bytes actually read.
+ virtual size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) = 0;
+
+ // Create a string representing the formatted line of backtrace information
+ // for a single frame.
+ virtual std::string FormatFrameData(size_t frame_num);
+ static std::string FormatFrameData(const backtrace_frame_data_t* frame);
+
+ pid_t Pid() const { return pid_; }
+ pid_t Tid() const { return tid_; }
+ size_t NumFrames() const { return frames_.size(); }
+
+ const backtrace_frame_data_t* GetFrame(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return nullptr;
+ }
+ return &frames_[frame_num];
+ }
+
+ typedef std::vector<backtrace_frame_data_t>::iterator iterator;
+ iterator begin() { return frames_.begin(); }
+ iterator end() { return frames_.end(); }
+
+ typedef std::vector<backtrace_frame_data_t>::const_iterator const_iterator;
+ const_iterator begin() const { return frames_.begin(); }
+ const_iterator end() const { return frames_.end(); }
+
+ BacktraceMap* GetMap() { return map_; }
+
+ BacktraceUnwindError GetError() { return error_; }
+
+ std::string GetErrorString(BacktraceUnwindError error);
+
+ // Set whether to skip frames in libbacktrace/libunwindstack when doing a local unwind.
+ void SetSkipFrames(bool skip_frames) { skip_frames_ = skip_frames; }
+
+ protected:
+ Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+
+ // The name returned is not demangled, GetFunctionName() takes care of
+ // demangling the name.
+ virtual std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) = 0;
+
+ virtual bool VerifyReadWordArgs(uint64_t ptr, word_t* out_value);
+
+ bool BuildMap();
+
+ pid_t pid_;
+ pid_t tid_;
+
+ BacktraceMap* map_;
+ bool map_shared_;
+
+ std::vector<backtrace_frame_data_t> frames_;
+
+ // Skip frames in libbacktrace/libunwindstack when doing a local unwind.
+ bool skip_frames_ = true;
+
+ BacktraceUnwindError error_;
+};
+
+#endif // _BACKTRACE_BACKTRACE_H
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
new file mode 100644
index 000000000..e000a00e2
--- /dev/null
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKTRACE_BACKTRACE_MAP_H
+#define _BACKTRACE_BACKTRACE_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef _WIN32
+// MINGW does not define these constants.
+#define PROT_NONE 0
+#define PROT_READ 0x1
+#define PROT_WRITE 0x2
+#define PROT_EXEC 0x4
+#else
+#include <sys/mman.h>
+#endif
+
+#include <deque>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+// Forward declaration.
+struct backtrace_stackinfo_t;
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int PROT_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int PROT_JIT_SYMFILE_MAP = 0x4000;
+
+struct backtrace_map_t {
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint64_t offset = 0;
+ uint64_t load_bias = 0;
+ int flags = 0;
+ std::string name;
+
+ // Returns `name` if non-empty, or `<anonymous:0x...>` otherwise.
+ std::string Name() const;
+};
+
+namespace unwindstack {
+class Memory;
+}
+
+class BacktraceMap {
+public:
+ // If uncached is true, then parse the current process map as of the call.
+ // Passing a map created with uncached set to true to Backtrace::Create()
+ // is unsupported.
+ static BacktraceMap* Create(pid_t pid, bool uncached = false);
+
+ virtual ~BacktraceMap();
+
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
+ public:
+ iterator(BacktraceMap* map, size_t index) : map_(map), index_(index) {}
+
+ iterator& operator++() {
+ index_++;
+ return *this;
+ }
+ const iterator operator++(int increment) {
+ index_ += increment;
+ return *this;
+ }
+ iterator& operator--() {
+ index_--;
+ return *this;
+ }
+ const iterator operator--(int decrement) {
+ index_ -= decrement;
+ return *this;
+ }
+
+ bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+ bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+ const backtrace_map_t* operator*() {
+ if (index_ >= map_->size()) {
+ return nullptr;
+ }
+ backtrace_map_t* map = &map_->maps_[index_];
+ if (map->load_bias == static_cast<uint64_t>(-1)) {
+ map->load_bias = map_->GetLoadBias(index_);
+ }
+ return map;
+ }
+
+ private:
+ BacktraceMap* map_ = nullptr;
+ size_t index_ = 0;
+ };
+
+ iterator begin() { return iterator(this, 0); }
+ iterator end() { return iterator(this, maps_.size()); }
+
+ // Fill in the map data structure for the given address.
+ virtual void FillIn(uint64_t addr, backtrace_map_t* map);
+
+ // Only supported with the new unwinder.
+ virtual std::string GetFunctionName(uint64_t /*pc*/, uint64_t* /*offset*/) { return ""; }
+ virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() { return nullptr; }
+
+ // The flags returned are the same flags as used by the mmap call.
+ // The values are PROT_*.
+ int GetFlags(uint64_t pc) {
+ backtrace_map_t map;
+ FillIn(pc, &map);
+ if (IsValid(map)) {
+ return map.flags;
+ }
+ return PROT_NONE;
+ }
+
+ bool IsReadable(uint64_t pc) { return GetFlags(pc) & PROT_READ; }
+ bool IsWritable(uint64_t pc) { return GetFlags(pc) & PROT_WRITE; }
+ bool IsExecutable(uint64_t pc) { return GetFlags(pc) & PROT_EXEC; }
+
+ // In order to use the iterators on this object, a caller must
+ // call the LockIterator and UnlockIterator function to guarantee
+ // that the data does not change while it's being used.
+ virtual void LockIterator() {}
+ virtual void UnlockIterator() {}
+
+ size_t size() const { return maps_.size(); }
+
+ virtual bool Build();
+
+ static inline bool IsValid(const backtrace_map_t& map) {
+ return map.end > 0;
+ }
+
+ void SetSuffixesToIgnore(std::vector<std::string> suffixes) {
+ suffixes_to_ignore_.insert(suffixes_to_ignore_.end(), suffixes.begin(), suffixes.end());
+ }
+
+ const std::vector<std::string>& GetSuffixesToIgnore() { return suffixes_to_ignore_; }
+
+ // Disabling the resolving of names results in the function name being
+ // set to an empty string and the function offset being set to zero
+ // in the frame data when unwinding.
+ void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
+ bool ResolveNames() { return resolve_names_; }
+
+ protected:
+ BacktraceMap(pid_t pid);
+
+ virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
+
+ pid_t pid_;
+ std::deque<backtrace_map_t> maps_;
+ std::vector<std::string> suffixes_to_ignore_;
+ bool resolve_names_ = true;
+};
+
+class ScopedBacktraceMapIteratorLock {
+public:
+ explicit ScopedBacktraceMapIteratorLock(BacktraceMap* map) : map_(map) {
+ map->LockIterator();
+ }
+
+ ~ScopedBacktraceMapIteratorLock() {
+ map_->UnlockIterator();
+ }
+
+private:
+ BacktraceMap* map_;
+};
+
+#endif // _BACKTRACE_BACKTRACE_MAP_H
diff --git a/libbacktrace/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
new file mode 100644
index 000000000..1a2da365a
--- /dev/null
+++ b/libbacktrace/include/backtrace/backtrace_constants.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BACKTRACE_BACKTRACE_CONSTANTS_H
+#define _BACKTRACE_BACKTRACE_CONSTANTS_H
+
+// When the pid to be traced is set to this value, then trace the current
+// process. If the tid value is not BACKTRACE_NO_TID, then the specified
+// thread from the current process will be traced.
+#define BACKTRACE_CURRENT_PROCESS (-1)
+// When the tid to be traced is set to this value, then trace the specified
+// current thread of the specified pid.
+#define BACKTRACE_CURRENT_THREAD (-1)
+
+#define MAX_BACKTRACE_FRAMES 256
+
+#endif // _BACKTRACE_BACKTRACE_CONSTANTS_H
diff --git a/libbacktrace/testdata/arm/libGLESv2_adreno.so b/libbacktrace/testdata/arm/libGLESv2_adreno.so
new file mode 100644
index 000000000..871f6dc89
--- /dev/null
+++ b/libbacktrace/testdata/arm/libGLESv2_adreno.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libandroid_runtime.so b/libbacktrace/testdata/arm/libandroid_runtime.so
new file mode 100644
index 000000000..e4283e63b
--- /dev/null
+++ b/libbacktrace/testdata/arm/libandroid_runtime.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libart.so b/libbacktrace/testdata/arm/libart.so
new file mode 100644
index 000000000..bed8e3595
--- /dev/null
+++ b/libbacktrace/testdata/arm/libart.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libbacktrace_test_arm_exidx.so b/libbacktrace/testdata/arm/libbacktrace_test_arm_exidx.so
new file mode 100755
index 000000000..454b0320a
--- /dev/null
+++ b/libbacktrace/testdata/arm/libbacktrace_test_arm_exidx.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libbacktrace_test_debug_frame.so b/libbacktrace/testdata/arm/libbacktrace_test_debug_frame.so
new file mode 100755
index 000000000..787f2cb94
--- /dev/null
+++ b/libbacktrace/testdata/arm/libbacktrace_test_debug_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.so b/libbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.so
new file mode 100755
index 000000000..9340d9882
--- /dev/null
+++ b/libbacktrace/testdata/arm/libbacktrace_test_gnu_debugdata.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata b/libbacktrace/testdata/arm/offline_testdata
new file mode 100644
index 000000000..d5b8f4718
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata
@@ -0,0 +1,105 @@
+pid: 32232 tid: 32233
+map: start: aad19000 end: aad6c000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test32
+map: start: aad6c000 end: aad6e000 offset: 52000 load_bias: 0 flags: 1 name: /data/backtrace_test32
+map: start: aad6e000 end: aad6f000 offset: 54000 load_bias: 0 flags: 3 name: /data/backtrace_test32
+map: start: e7380000 end: e7400000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e745f000 end: e7463000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libnetd_client.so
+map: start: e7463000 end: e7464000 offset: 3000 load_bias: 0 flags: 1 name: /system/lib/libnetd_client.so
+map: start: e7464000 end: e7465000 offset: 4000 load_bias: 0 flags: 3 name: /system/lib/libnetd_client.so
+map: start: e7480000 end: e7500000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e7558000 end: e756c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libunwind.so
+map: start: e756c000 end: e756d000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e756d000 end: e756e000 offset: 14000 load_bias: 0 flags: 1 name: /system/lib/libunwind.so
+map: start: e756e000 end: e756f000 offset: 15000 load_bias: 0 flags: 3 name: /system/lib/libunwind.so
+map: start: e756f000 end: e75b5000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e75d4000 end: e75e1000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbase.so
+map: start: e75e1000 end: e75e2000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libbase.so
+map: start: e75e2000 end: e75e3000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libbase.so
+map: start: e7600000 end: e7616000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblzma.so
+map: start: e7616000 end: e7617000 offset: 15000 load_bias: 0 flags: 1 name: /system/lib/liblzma.so
+map: start: e7617000 end: e7618000 offset: 16000 load_bias: 0 flags: 3 name: /system/lib/liblzma.so
+map: start: e7618000 end: e761d000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7647000 end: e7656000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblog.so
+map: start: e7656000 end: e7657000 offset: e000 load_bias: 0 flags: 1 name: /system/lib/liblog.so
+map: start: e7657000 end: e7658000 offset: f000 load_bias: 0 flags: 3 name: /system/lib/liblog.so
+map: start: e7681000 end: e76a2000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libm.so
+map: start: e76a2000 end: e76a3000 offset: 20000 load_bias: 0 flags: 1 name: /system/lib/libm.so
+map: start: e76a3000 end: e76a4000 offset: 21000 load_bias: 0 flags: 3 name: /system/lib/libm.so
+map: start: e76eb000 end: e76ee000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: e76ee000 end: e76ef000 offset: 2000 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: e76ef000 end: e76f0000 offset: 3000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: e7712000 end: e771f000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbacktrace.so
+map: start: e771f000 end: e7720000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e7720000 end: e7721000 offset: d000 load_bias: 0 flags: 1 name: /system/lib/libbacktrace.so
+map: start: e7721000 end: e7722000 offset: e000 load_bias: 0 flags: 3 name: /system/lib/libbacktrace.so
+map: start: e7761000 end: e7778000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libutils.so
+map: start: e7778000 end: e7779000 offset: 16000 load_bias: 0 flags: 1 name: /system/lib/libutils.so
+map: start: e7779000 end: e777a000 offset: 17000 load_bias: 0 flags: 3 name: /system/lib/libutils.so
+map: start: e77a5000 end: e782d000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libc.so
+map: start: e782d000 end: e7831000 offset: 87000 load_bias: 0 flags: 1 name: /system/lib/libc.so
+map: start: e7831000 end: e7833000 offset: 8b000 load_bias: 0 flags: 3 name: /system/lib/libc.so
+map: start: e7833000 end: e7834000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7834000 end: e7835000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: e7835000 end: e783b000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7845000 end: e8437000 offset: 0 load_bias: 2b000 flags: 5 name: /system/lib/libLLVM.so
+map: start: e8437000 end: e8438000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e8438000 end: e848a000 offset: bf2000 load_bias: 0 flags: 1 name: /system/lib/libLLVM.so
+map: start: e848a000 end: e848b000 offset: c44000 load_bias: 0 flags: 3 name: /system/lib/libLLVM.so
+map: start: e848b000 end: e84a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e84eb000 end: e84f7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libcutils.so
+map: start: e84f7000 end: e84f8000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e84f8000 end: e84f9000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libcutils.so
+map: start: e84f9000 end: e84fa000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libcutils.so
+map: start: e852e000 end: e85b3000 offset: 0 load_bias: 2000 flags: 5 name: /system/lib/libc++.so
+map: start: e85b3000 end: e85b4000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e85b4000 end: e85b8000 offset: 85000 load_bias: 0 flags: 1 name: /system/lib/libc++.so
+map: start: e85b8000 end: e85b9000 offset: 89000 load_bias: 0 flags: 3 name: /system/lib/libc++.so
+map: start: e85b9000 end: e85ba000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e85ce000 end: e85cf000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e85e4000 end: e85e5000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e8607000 end: e8608000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e8680000 end: e8700000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e870d000 end: e8719000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: e8719000 end: e871b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e871b000 end: e873b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: e873b000 end: e875b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e875b000 end: e875c000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e875c000 end: e875d000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e875d000 end: e875e000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e875e000 end: e875f000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e875f000 end: e877f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: e877f000 end: e879f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e879f000 end: e87a0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a0000 end: e87a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a1000 end: e87a2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a2000 end: e87a3000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e87a3000 end: e87a4000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: e87a4000 end: e87a5000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: e87a5000 end: e87a6000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: e87a6000 end: e87a7000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e87a7000 end: e87a8000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a8000 end: e87a9000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a9000 end: e87aa000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87aa000 end: e87ab000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87ab000 end: e87ac000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e87ac000 end: e87ad000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: e87ad000 end: e87af000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: e87af000 end: e87b0000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e87b0000 end: e880d000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker
+map: start: e880d000 end: e880f000 offset: 5c000 load_bias: 0 flags: 1 name: /system/bin/linker
+map: start: e880f000 end: e8810000 offset: 5e000 load_bias: 0 flags: 3 name: /system/bin/linker
+map: start: e8810000 end: e8812000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: e8812000 end: e8813000 offset: 0 load_bias: 0 flags: 1 name:
+map: start: e8813000 end: e8815000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: ff886000 end: ff8a9000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: ffff0000 end: ffff1000 offset: 0 load_bias: 0 flags: 5 name: [vectors]
+ucontext: 104 000000000000000000000000000000000000000000000000000000000000000034868affdc8871e8150000001c0000001c000000150000000e00000007000000e08771e834868aff2354d2aa24f9ffffdc8871e88c8771e875b86ee778ba6ee70000000000000000
+stack: start: e8715000 end: e8719000 size: 16384 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc8871e87dba6ee734868affdc8871e8dc8871e85dba6ee7070000000e000000150000001c000000dc8871e85dba6ee71c000000150000000e00000007000000100000000c0000000800000004000000ddb86ee75dba6ee7dc8871e804000000080000000c00000010000000dc8871e85dba6ee7100000000c000000080000000400000008000000060000000400000002000000288871e835b96ee75dba6ee7dc8871e802000000040000000600000008000000dc8871e85dba6ee70800000006000000040000000200000004000000030000000200000001000000708871e88db96ee75dba6ee7dc8871e801000000020000000300000004000000dc8871e85dba6ee70400000003000000020000000100000004000000208971e8208971e878000000e87d00003dba6ee75dba6ee7dc8871e878000000c5807ce7fc7183e734868aff78868aff78868aff34868aff34868aff78868affe0879437208971e84154d2aa0020000034868aff34868aff34868aff78000000c9b87ee7b1b87ee7a3f47be7288971e8b1b87ee7208971e800000000f83481e800000000e97d0000e87d000000000000005071e82039000000100000000000000000000000000000000000002354d2aa34868aff00000000002071e801000000000000000000000000000000708971e8208971e8000000000000000000000000e0879437000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: e76eb835 name: unknown_start
+function: start: e76eb835 end: e76eb88d name: test_level_four
+function: start: e76eb88d end: e76eb8e5 name: test_level_three
+function: start: e76eb8e5 end: e76eb93d name: test_level_two
+function: start: e76eb93d end: e76eb995 name: test_level_one
+function: start: e76eb995 end: e76eb9f1 name: test_recursive_call
+function: start: e76eb9f1 end: ffffffff name: test_get_context_and_wait
+function: start: ffffffff end: ffffffff name: unknown_end
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno b/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno
new file mode 100644
index 000000000..d7c186e6f
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno
@@ -0,0 +1,6 @@
+pid: 7288 tid: 31656
+ucontext: 104 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f017cc00000000356241cc0000000000000000
+map: start: cc361000 end: cc758000 offset: 0 load_bias: 9000 flags: 5 name: /vendor/lib/egl/libGLESv2_adreno.so
+stack: start: cc17f234 end: cc17f258 size: 36 0000000000000000000000000000000000000000000000000000000000000000b36141cc
+function: start: be1f0 end: be304 name: EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)
+function: start: be058 end: be1f0 name: EsxContext::ClearBuffersForDebug()
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime b/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime
new file mode 100644
index 000000000..54f352520
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime
@@ -0,0 +1,6 @@
+pid: 7288 tid: 31656
+ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003069fed80000000049dcf6f10000000000000000
+map: start: f1f10000 end: f2049000 offset: 0 load_bias: 10000 flags: 5 name: /system/lib/libandroid_runtime.so
+stack: start: d8fe6948 end: d8fe6958 size: 16 000000000000000000000000e7dcf6f1
+function: start: 6dbf9 end: 6dce5 name: android::AndroidRuntime::javaThreadShell
+function: start: 6dce5 end: 6dd79 name: android::AndroidRuntime::javaCreateThreadEtc
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
new file mode 100644
index 000000000..c1369ff6c
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -0,0 +1,10 @@
+pid: 32232 tid: 32233
+ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e90000000000000000
+map: start: e9380000 end: e9766000 offset: 0 load_bias: b000 flags: 5 name: /system/lib/libart.so
+stack: start: ffd12dc0 end: ffd1306c size: 684 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d89524
+function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
+function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
+function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
+function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
+function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
+function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
diff --git a/libbacktrace/testdata/arm64/eglSubDriverAndroid.so b/libbacktrace/testdata/arm64/eglSubDriverAndroid.so
new file mode 100644
index 000000000..10ce06bfd
--- /dev/null
+++ b/libbacktrace/testdata/arm64/eglSubDriverAndroid.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
new file mode 100755
index 000000000..880f33796
--- /dev/null
+++ b/libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/libskia.so b/libbacktrace/testdata/arm64/libskia.so
new file mode 100644
index 000000000..ef1a6a185
--- /dev/null
+++ b/libbacktrace/testdata/arm64/libskia.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/offline_testdata b/libbacktrace/testdata/arm64/offline_testdata
new file mode 100644
index 000000000..cee9f7202
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata
@@ -0,0 +1,107 @@
+pid: 32438 tid: 32439
+map: start: 557066e000 end: 55706ee000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test64
+map: start: 55706ef000 end: 55706f2000 offset: 80000 load_bias: 0 flags: 1 name: /data/backtrace_test64
+map: start: 55706f2000 end: 55706f3000 offset: 83000 load_bias: 0 flags: 3 name: /data/backtrace_test64
+map: start: 7014200000 end: 7014600000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: 701464c000 end: 701465c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libcutils.so
+map: start: 701465c000 end: 701465d000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 701465d000 end: 701465e000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/libcutils.so
+map: start: 701465e000 end: 701465f000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/libcutils.so
+map: start: 7014691000 end: 70146b5000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblzma.so
+map: start: 70146b5000 end: 70146b6000 offset: 23000 load_bias: 0 flags: 1 name: /system/lib64/liblzma.so
+map: start: 70146b6000 end: 70146b7000 offset: 24000 load_bias: 0 flags: 3 name: /system/lib64/liblzma.so
+map: start: 70146b7000 end: 70146bc000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70146c9000 end: 70158b5000 offset: 0 load_bias: af000 flags: 5 name: /system/lib64/libLLVM.so
+map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_bias: 0 flags: 1 name: /system/lib64/libLLVM.so
+map: start: 701596b000 end: 701596c000 offset: 12a1000 load_bias: 0 flags: 3 name: /system/lib64/libLLVM.so
+map: start: 701596c000 end: 701599f000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70159c2000 end: 70159f9000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libm.so
+map: start: 70159f9000 end: 70159fa000 offset: 36000 load_bias: 0 flags: 1 name: /system/lib64/libm.so
+map: start: 70159fa000 end: 70159fb000 offset: 37000 load_bias: 0 flags: 3 name: /system/lib64/libm.so
+map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbacktrace.so
+map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbacktrace.so
+map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbacktrace.so
+map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_bias: 1000 flags: 5 name: /system/lib64/libutils.so
+map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_bias: 0 flags: 1 name: /system/lib64/libutils.so
+map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_bias: 0 flags: 3 name: /system/lib64/libutils.so
+map: start: 7015a99000 end: 7015b6d000 offset: 0 load_bias: 9000 flags: 5 name: /system/lib64/libc++.so
+map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_bias: 0 flags: 1 name: /system/lib64/libc++.so
+map: start: 7015b76000 end: 7015b77000 offset: dc000 load_bias: 0 flags: 3 name: /system/lib64/libc++.so
+map: start: 7015b77000 end: 7015b7a000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015b81000 end: 7015b92000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblog.so
+map: start: 7015b92000 end: 7015b93000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/liblog.so
+map: start: 7015b93000 end: 7015b94000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/liblog.so
+map: start: 7015be3000 end: 7015ca3000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libc.so
+map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_bias: 0 flags: 1 name: /system/lib64/libc.so
+map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_bias: 0 flags: 3 name: /system/lib64/libc.so
+map: start: 7015cab000 end: 7015cac000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cac000 end: 7015cad000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: 7015cad000 end: 7015cb4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: 7015d1f000 end: 7015d39000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libunwind.so
+map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_bias: 0 flags: 1 name: /system/lib64/libunwind.so
+map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_bias: 0 flags: 3 name: /system/lib64/libunwind.so
+map: start: 7015d3b000 end: 7015da4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015de8000 end: 7015df7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbase.so
+map: start: 7015df7000 end: 7015df8000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7015df8000 end: 7015df9000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbase.so
+map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbase.so
+map: start: 7015e35000 end: 7015e36000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015e4f000 end: 7015e50000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015f11000 end: 7015f13000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libnetd_client.so
+map: start: 7015f13000 end: 7015f14000 offset: 1000 load_bias: 0 flags: 1 name: /system/lib64/libnetd_client.so
+map: start: 7015f14000 end: 7015f15000 offset: 2000 load_bias: 0 flags: 3 name: /system/lib64/libnetd_client.so
+map: start: 7015f6c000 end: 7015f79000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7015f79000 end: 7015f99000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: 7015f99000 end: 7015f9a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015faf000 end: 7015fcf000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7015fdd000 end: 7015fde000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015fde000 end: 7015ffe000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: 7015ffe000 end: 701601e000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 701601e000 end: 701601f000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 701601f000 end: 7016020000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7016020000 end: 7016021000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7016021000 end: 7016022000 offset: 0 load_bias: 0 flags: 0 name:
+map: start: 7016022000 end: 7016023000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: 7016023000 end: 7016025000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7016025000 end: 7016026000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016026000 end: 7016027000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7016027000 end: 7016028000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016028000 end: 7016029000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016029000 end: 701602a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 701602a000 end: 701602b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 701602b000 end: 701602c000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: 701602c000 end: 7016030000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: 7016030000 end: 7016031000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016031000 end: 7016033000 offset: 0 load_bias: 0 flags: 5 name: [vdso]
+map: start: 7016033000 end: 70160dd000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker64
+map: start: 70160dd000 end: 70160e0000 offset: a9000 load_bias: 0 flags: 1 name: /system/bin/linker64
+map: start: 70160e0000 end: 70160e1000 offset: ac000 load_bias: 0 flags: 3 name: /system/bin/linker64
+map: start: 70160e1000 end: 70160e4000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 70160e4000 end: 70160e5000 offset: 0 load_bias: 0 flags: 1 name:
+map: start: 70160e5000 end: 70160e8000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+ucontext: 464 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000000e0000000000000015000000000000001c00000000000000ec59cf1570000000b863fd15700000005064fd15700000000000000000000000ec59cf15700000000200000000000000b863fd1570000000144abed87f0000006064fd15700000005064fd157000000000010000000000005826bed87f000000d86fcf15700000006057cf157000000000000000000000005064fd15700000005064fd15700000005064fd1570000000b67e00000000000040fd677055000000d064fd15700000000030fd157000000002000000000000000100000000000000fcb58a56000000000063fd15700000009857cf1570000000c062fd15700000001c5acf157000000000000000000000000000000000000000
+stack: start: 7015fd3000 end: 7015fd7000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000004038bed87f000000b863fd1570000000b863fd1570000000b863fd1570000000ec59cf15700000001c000000150000000e000000070000003063fd15700000001c58cf1570000000b863fd1570000000ec59cf1570000000100000000c00000008000000040000006063fd15700000007c58cf1570000000b863fd1570000000ec59cf1570000000080000000600000004000000020000009063fd1570000000dc58cf1570000000b863fd1570000000ec59cf157000000004000000030000000200000001000000d063fd1570000000c459cf15700000000100000000000000144abed87f0000004038bed87f0000004038bed87f000000144abed87f000000d3aec914588f4bcd1064fd157000000074fd6770550000004038bed87f0000004038bed87f000000ec84c41570000000e484c41570000000c484c4157000000000000000000000004064fd15700000004015c01570000000b67e0000000000000000000000000000705a0e1670000000185b0e167000000000000000000000000000000000000000705a0e16700000000000000000000000b77e0000b67e000000000000550000000030fd157000000050340000000000000010000000000000000000000000000000b222147000000000102a14700000000000000000000000000000000000000040fd6770550000004038bed87f000000000000000000000000a0fa1570000000010000000000000000000000000000000000000000000000e864fd15700000005064fd1570000000000000000000000000000000000000000000000000000000d3aec914588f4bcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: 7015cf5760 name: unknown_start
+function: start: 7015cf5760 end: 7015cf57cc name: test_level_four
+function: start: 7015cf57cc end: 7015cf582c name: test_level_three
+function: start: 7015cf582c end: 7015cf588c name: test_level_two
+function: start: 7015cf588c end: 7015cf58ec name: test_level_one
+function: start: 7015cf58ec end: 7015cf5968 name: test_recursive_call
+function: start: 7015cf5968 end: ffffffffffffffff name: test_get_context_and_wait
+function: start: ffffffffffffffff end: ffffffffffffffff name: unknown_end
diff --git a/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid b/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid
new file mode 100644
index 000000000..673e30e1d
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid
@@ -0,0 +1,6 @@
+pid: 12276 tid: 12303
+map: start: 7b8c01e000 end: 7b8c030000 offset: 0 load_bias: 0 flags: 5 name: /vendor/lib64/egl/eglSubDriverAndroid.so
+ucontext: 464 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004070158c7b00000000000000000000001070158c7b000000647f028c7b00000000000000000000000000000000000000
+stack: start: 7b8c157020 end: 7b8c157050 size: 48 00000000000000000000000000000000000000000000000000000000000000000000000000000000547e028c7b000000
+function: start: 9ed8 end: a1b0 name: EglAndroidWindowSurface::Initialize(EglAndroidConfig*, int const*)
+function: start: 9dcc end: 9ed8 name: EglAndroidWindowSurface::Create(ANativeWindow*, EglAndroidConfig*, EglAndroidWindowSurface**, int const*)
diff --git a/libbacktrace/testdata/arm64/offline_testdata_for_libskia b/libbacktrace/testdata/arm64/offline_testdata_for_libskia
new file mode 100644
index 000000000..da820c0bd
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata_for_libskia
@@ -0,0 +1,6 @@
+pid: 32232 tid: 32233
+map: start: 7c24c80000 end: 7c25413000 offset: 0 load_bias: 5f000 flags: 5 name: /system/lib64/libskia.so
+ucontext: 464 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b04b158c7b0000000000000000000000504b158c7b0000000c9a18257c00000000000000000000000000000000000000
+stack: start: 7b8c154b80 end: 7b8c154bc0 size: 64 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec43f2247c000000
+function: start: 568970 end: 568c08 name: SkScalerContext_FreeType::generateImage(SkGlyph const&)
+function: start: 30330c end: 3044b0 name: SkScalerContext::getImage(SkGlyph const&)
diff --git a/libbacktrace/testdata/x86/libbacktrace_test_debug_frame.so b/libbacktrace/testdata/x86/libbacktrace_test_debug_frame.so
new file mode 100755
index 000000000..a6f3b29f3
--- /dev/null
+++ b/libbacktrace/testdata/x86/libbacktrace_test_debug_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.so b/libbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.so
new file mode 100755
index 000000000..ea58dfbe5
--- /dev/null
+++ b/libbacktrace/testdata/x86/libbacktrace_test_gnu_debugdata.so
Binary files differ
diff --git a/libbacktrace/testdata/x86/offline_testdata b/libbacktrace/testdata/x86/offline_testdata
new file mode 100644
index 000000000..920b3385b
--- /dev/null
+++ b/libbacktrace/testdata/x86/offline_testdata
@@ -0,0 +1,82 @@
+pid: 34545 tid: 34546
+map: start: f705a000 end: f705c000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f705c000 end: f707f000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f707f000 end: f7080000 offset: 22000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7080000 end: f7081000 offset: 23000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7081000 end: f7088000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7088000 end: f7230000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7230000 end: f7231000 offset: 1a8000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7231000 end: f7233000 offset: 1a8000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7233000 end: f7234000 offset: 1aa000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7234000 end: f7237000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7237000 end: f727b000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727b000 end: f727c000 offset: 43000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727c000 end: f727d000 offset: 44000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727d000 end: f7299000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f7299000 end: f729a000 offset: 1b000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f729a000 end: f72b8000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b8000 end: f72b9000 offset: 1e000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b9000 end: f72bb000 offset: 1e000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bb000 end: f72bc000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bc000 end: f72bd000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f72bd000 end: f72e0000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e0000 end: f72e1000 offset: 22000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e1000 end: f72e2000 offset: 23000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e2000 end: f72e5000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e5000 end: f72e6000 offset: 2000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e6000 end: f72e7000 offset: 3000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e7000 end: f72ee000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ee000 end: f72ef000 offset: 6000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ef000 end: f72f0000 offset: 7000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72f0000 end: f7308000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7308000 end: f7309000 offset: 18000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7309000 end: f730a000 offset: 19000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f730a000 end: f730c000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f732f000 end: f7331000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7331000 end: f7425000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7425000 end: f7426000 offset: f4000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7426000 end: f742a000 offset: f4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742a000 end: f742b000 offset: f8000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742b000 end: f742d000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f742d000 end: f7446000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7446000 end: f7447000 offset: 18000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7447000 end: f7448000 offset: 19000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7448000 end: f7457000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7457000 end: f745c000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745c000 end: f745d000 offset: 4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745d000 end: f745e000 offset: 5000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745e000 end: f7467000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7467000 end: f7468000 offset: 9000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7468000 end: f7469000 offset: 9000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7469000 end: f746a000 offset: a000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f746a000 end: f7477000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7477000 end: f7478000 offset: c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7478000 end: f7479000 offset: d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7479000 end: f7489000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f7489000 end: f748a000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748a000 end: f748b000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748b000 end: f748c000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f748c000 end: f748d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748d000 end: f748e000 offset: 0 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748e000 end: f748f000 offset: 1000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748f000 end: f7491000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7491000 end: f74b1000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b1000 end: f74b2000 offset: 1f000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b2000 end: f74b3000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b3000 end: f77c6000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77c6000 end: f77c7000 offset: 0 load_bias: ffffe000 flags: 5 name: [vdso]
+map: start: f77c7000 end: f77d4000 offset: 313000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d4000 end: f77d5000 offset: 320000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d5000 end: f77d6000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: f7ec6000 end: f7ee7000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: ffe4e000 end: ffe70000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+ucontext: 96 0000000000000000000000000000000000000000abdae6ff00000000afdae6ff78dae6ff150000001c000000b8f132f7a0f132f7d0df48f7a0ca48f728d9e6ff000000000000000000000000ceca48f7a8dae6ff000000000000000000000000
+stack: start: f732c000 end: f7330000 size: 16384 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d9d49f761b009f71e382ff7000000000000000000000000000000000000000002000000f6372ff704c82bf70000000000204bf7f0a908f7ceca48f728d9e6fff4a449f70000000020f332f720f332f7d0df48f7f8f132f794c748f720f332f70c144205978142a8d4be08f7d0df48f720f332f7a0ca48f71c000000150000000e000000070000001c000000a0ca48f7d0df48f748f232f739c848f7070000000e000000150000001c000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7100000000c000000080000000400000010000000a0ca48f7d0df48f798f232f7c9c848f704000000080000000c00000010000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f70800000006000000040000000200000008000000a0ca48f7d0df48f7e8f232f759c948f702000000040000000600000008000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7040000000300000002000000010000000046bdaa00000000d0df48f738f332f77cca48f701000000020000000300000004000000a0ca48f720f332f700000000f83b7df7696d4df7d0df48f788dae6ff28d9e6ff28d9e6ff88dae6ff58f332f70046bdaa40fb32f7f83b7df758f332f78d6d4df728d9e6ff88dae6fff83b7df728d9e6ff28d9e6ff009030f728f432f7726f2ff728d9e6ff40fb32f740fb32f740fb32f790f332f700000000000000000000000000000000000000000000000000000000009030f740fb32f7000f3d0028f432f703b12c75032f144e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a06e2ff700000000000f3d00000000008e3f17f740fb32f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03823f7c4fd32f700000000e0341df7e02e1df7e03d1df70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040fb32f7188eecf740fb32f70100000030647cf70046bdaa28658876000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a705f7a4b130f7f2860000f1860000b0fb32f7ecffffff000000000000000090f332f7000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccfb32f70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c2638cfa7f3e1d0000000000000000000000000000000000406d4df728d9e6ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c032f7004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: f748c740 name: unknown_start
+function: start: f748c740 end: f748c7c0 name: test_level_four
+function: start: f748c7c0 end: f748c850 name: test_level_three
+function: start: f748c850 end: f748c8e0 name: test_level_two
+function: start: f748c8e0 end: f748c970 name: test_level_one
+function: start: f748c970 end: f748ca10 name: test_recursive_call
+function: start: f748ca10 end: ffffffff name: test_get_context_and_wait
+function: start: ffffffff end: ffffffff name: unknown_end
diff --git a/libbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.so
new file mode 100755
index 000000000..f11665830
--- /dev/null
+++ b/libbacktrace/testdata/x86_64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/x86_64/offline_testdata b/libbacktrace/testdata/x86_64/offline_testdata
new file mode 100644
index 000000000..c6bb24161
--- /dev/null
+++ b/libbacktrace/testdata/x86_64/offline_testdata
@@ -0,0 +1,93 @@
+pid: 25683 tid: 25692
+map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_bias: 0 flags: 3 name:
+map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_bias: ffffffffff700000 flags: 5 name: [vdso]
+map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_bias: 0 flags: 5 name: [vsyscall]
+ucontext: 224 00000000000000000000000000000000000000000000000000000000000000000000000000000000b07bcfabd57f000098deb6abd57f0000b82455add57f0000010000000000000000000000000000000000000000000000c0e354add57f000000e7b6abd57f0000c8b080f4fc7f00000e0000000000000080ddb6abd57f000000000000000000001500000000000000b07bcfabd57f00001c0000000000000060ddb6abd57f0000d07bcfabd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+stack: start: 7fd5abb6b000 end: 7fd5abb6f000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c4eaeabd57f00000000000000000000978142a8000000000f0000000000000012000000000000003888b4abd57f0000e657aeabd57f00000000000000000000a0dcb6abd57f0000307d78aad57f0000b0ddb6abd57f000028d978aad57f0000060aa10200000000a0ddb6abd57f0000000000000000000000000000000000002091b1abd57f0000e094b4abd57f000017008cabd57f0000c84d79aad57f000028e28babd57f00000000000005000000d503000001000000000000000000000068deb6abd57f000040deb6abd57f00002091b1abd57f00000100000000000000e0f3c6abd57f000088f0c6abd57f00006159aeabd57f000000000000000000002091b1abd57f000005000000000000000000000000000000010000000000000088f0c6abd57f00000000000000000000d07bcfabd57f00000000000000000000000000000000000098deb6abd57f000098deb6abd57f0000b0ddb6abd57f00006179cfabd57f000098deb6abd57f0000b07bcfabd57f00001c000000150000000e00000007000000f0ddb6abd57f0000e179cfabd57f00000000000000000000150000001c00000098deb6abd57f0000b07bcfabd57f0000100000000c000000080000000400000030deb6abd57f0000417acfabd57f000000000000000000000c0000001000000098deb6abd57f0000b07bcfabd57f00000800000006000000040000000200000070deb6abd57f0000a17acfabd57f00000000000000000000060000000800000098deb6abd57f0000b07bcfabd57f000004000000030000000200000001000000b0deb6abd57f0000817bcfabd57f0000000000000000000074b480f4fc7f0000c8b080f4fc7f0000c8b080f4fc7f000074b480f4fc7f000000006a80f3f73cf1d0deb6abd57f00002a52d5abd57f0000c8b080f4fc7f0000c8b080f4fc7f0000000000000000000084518cabd57f0000000000000000000000e7b6abd57f000000e7b6abd57f00008f990b1e3bad5a6700000000000000000000000000000000c0e354add57f000000e7b6abd57f00008f99cba356faf1988f9991bc23faf1980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f00007de387aad57f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006030b4aad57f0000b8edb6abd57f00000000000000000000000000000000000080b88eaad57f0000000000000000000080b28eaad57f0000000000000000000080c18eaad57f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f0000402555add57f000000e7b6abd57f00000100000000000000000000000000000000006a80f3f73cf1058f9d56adb3c7cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000407ab1abd57f000030a3adabd57f00005c64000053640000e0e9b6abd57f0000e0e9b6abd57f0000e0ffffffffffffff00000000000000000000000000000000f0deb6abd57f00000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010eab6abd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c46ad90f52391d00000000000000000000000000000000000000000000000000f051d5abd57f0000c8b080f4fc7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0b6abd57f000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: 7fd5abcf7930 name: unknown_start
+function: start: 7fd5abcf7930 end: 7fd5abcf7990 name: test_level_four
+function: start: 7fd5abcf7990 end: 7fd5abcf79f0 name: test_level_three
+function: start: 7fd5abcf79f0 end: 7fd5abcf7a50 name: test_level_two
+function: start: 7fd5abcf7a50 end: 7fd5abcf7ab0 name: test_level_one
+function: start: 7fd5abcf7ab0 end: 7fd5abcf7b30 name: test_recursive_call
+function: start: 7fd5abcf7b30 end: ffffffffffffffff name: test_get_context_and_wait
+function: start: ffffffffffffffff end: ffffffffffffffff name: unknown_end
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
index 75f43eeaa..d2487e20e 100644
--- a/libbinderwrapper/Android.bp
+++ b/libbinderwrapper/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "libbinderwrapper_defaults",
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index b33d46d50..d7175e0be 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libcrypto_utils",
vendor_available: true,
@@ -27,7 +23,7 @@ cc_library {
},
host_supported: true,
srcs: [
- "android_pubkey.cpp",
+ "android_pubkey.c",
],
cflags: [
"-Wall",
diff --git a/libcrypto_utils/android_pubkey.c b/libcrypto_utils/android_pubkey.c
new file mode 100644
index 000000000..3052e52bf
--- /dev/null
+++ b/libcrypto_utils/android_pubkey.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <crypto_utils/android_pubkey.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+
+// Better safe than sorry.
+#if (ANDROID_PUBKEY_MODULUS_SIZE % 4) != 0
+#error RSA modulus size must be multiple of the word size!
+#endif
+
+// Size of the RSA modulus in words.
+#define ANDROID_PUBKEY_MODULUS_SIZE_WORDS (ANDROID_PUBKEY_MODULUS_SIZE / 4)
+
+// This file implements encoding and decoding logic for Android's custom RSA
+// public key binary format. Public keys are stored as a sequence of
+// little-endian 32 bit words. Note that Android only supports little-endian
+// processors, so we don't do any byte order conversions when parsing the binary
+// struct.
+typedef struct RSAPublicKey {
+ // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
+ uint32_t modulus_size_words;
+
+ // Precomputed montgomery parameter: -1 / n[0] mod 2^32
+ uint32_t n0inv;
+
+ // RSA modulus as a little-endian array.
+ uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
+
+ // Montgomery parameter R^2 as a little-endian array of little-endian words.
+ uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
+
+ // RSA modulus: 3 or 65537
+ uint32_t exponent;
+} RSAPublicKey;
+
+// Reverses byte order in |buffer|.
+static void reverse_bytes(uint8_t* buffer, size_t size) {
+ for (size_t i = 0; i < (size + 1) / 2; ++i) {
+ uint8_t tmp = buffer[i];
+ buffer[i] = buffer[size - i - 1];
+ buffer[size - i - 1] = tmp;
+ }
+}
+
+bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
+ const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
+ bool ret = false;
+ uint8_t modulus_buffer[ANDROID_PUBKEY_MODULUS_SIZE];
+ RSA* new_key = RSA_new();
+ if (!new_key) {
+ goto cleanup;
+ }
+
+ // Check |size| is large enough and the modulus size is correct.
+ if (size < sizeof(RSAPublicKey)) {
+ goto cleanup;
+ }
+ if (key_struct->modulus_size_words != ANDROID_PUBKEY_MODULUS_SIZE_WORDS) {
+ goto cleanup;
+ }
+
+ // Convert the modulus to big-endian byte order as expected by BN_bin2bn.
+ memcpy(modulus_buffer, key_struct->modulus, sizeof(modulus_buffer));
+ reverse_bytes(modulus_buffer, sizeof(modulus_buffer));
+ new_key->n = BN_bin2bn(modulus_buffer, sizeof(modulus_buffer), NULL);
+ if (!new_key->n) {
+ goto cleanup;
+ }
+
+ // Read the exponent.
+ new_key->e = BN_new();
+ if (!new_key->e || !BN_set_word(new_key->e, key_struct->exponent)) {
+ goto cleanup;
+ }
+
+ // Note that we don't extract the montgomery parameters n0inv and rr from
+ // the RSAPublicKey structure. They assume a word size of 32 bits, but
+ // BoringSSL may use a word size of 64 bits internally, so we're lacking the
+ // top 32 bits of n0inv in general. For now, we just ignore the parameters
+ // and have BoringSSL recompute them internally. More sophisticated logic can
+ // be added here if/when we want the additional speedup from using the
+ // pre-computed montgomery parameters.
+
+ *key = new_key;
+ ret = true;
+
+cleanup:
+ if (!ret && new_key) {
+ RSA_free(new_key);
+ }
+ return ret;
+}
+
+static bool android_pubkey_encode_bignum(const BIGNUM* num, uint8_t* buffer) {
+ if (!BN_bn2bin_padded(buffer, ANDROID_PUBKEY_MODULUS_SIZE, num)) {
+ return false;
+ }
+
+ reverse_bytes(buffer, ANDROID_PUBKEY_MODULUS_SIZE);
+ return true;
+}
+
+bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
+ RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
+ bool ret = false;
+ BN_CTX* ctx = BN_CTX_new();
+ BIGNUM* r32 = BN_new();
+ BIGNUM* n0inv = BN_new();
+ BIGNUM* rr = BN_new();
+
+ if (sizeof(RSAPublicKey) > size ||
+ RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
+ goto cleanup;
+ }
+
+ // Store the modulus size.
+ key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
+
+ // Compute and store n0inv = -1 / N[0] mod 2^32.
+ if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) ||
+ !BN_mod(n0inv, key->n, r32, ctx) ||
+ !BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
+ goto cleanup;
+ }
+ key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
+
+ // Store the modulus.
+ if (!android_pubkey_encode_bignum(key->n, key_struct->modulus)) {
+ goto cleanup;
+ }
+
+ // Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
+ if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
+ !BN_mod_sqr(rr, rr, key->n, ctx) ||
+ !android_pubkey_encode_bignum(rr, key_struct->rr)) {
+ goto cleanup;
+ }
+
+ // Store the exponent.
+ key_struct->exponent = (uint32_t)BN_get_word(key->e);
+
+ ret = true;
+
+cleanup:
+ BN_free(rr);
+ BN_free(n0inv);
+ BN_free(r32);
+ BN_CTX_free(ctx);
+ return ret;
+}
diff --git a/libcrypto_utils/android_pubkey.cpp b/libcrypto_utils/android_pubkey.cpp
deleted file mode 100644
index 21e5663ee..000000000
--- a/libcrypto_utils/android_pubkey.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <crypto_utils/android_pubkey.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <openssl/bn.h>
-
-// Better safe than sorry.
-#if (ANDROID_PUBKEY_MODULUS_SIZE % 4) != 0
-#error RSA modulus size must be multiple of the word size!
-#endif
-
-// Size of the RSA modulus in words.
-#define ANDROID_PUBKEY_MODULUS_SIZE_WORDS (ANDROID_PUBKEY_MODULUS_SIZE / 4)
-
-// This file implements encoding and decoding logic for Android's custom RSA
-// public key binary format. Public keys are stored as a sequence of
-// little-endian 32 bit words. Note that Android only supports little-endian
-// processors, so we don't do any byte order conversions when parsing the binary
-// struct.
-struct RSAPublicKey {
- // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
- uint32_t modulus_size_words;
-
- // Precomputed montgomery parameter: -1 / n[0] mod 2^32
- uint32_t n0inv;
-
- // RSA modulus as a little-endian array.
- uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
-
- // Montgomery parameter R^2 as a little-endian array.
- uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
-
- // RSA modulus: 3 or 65537
- uint32_t exponent;
-};
-
-bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
- const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
- bool ret = false;
- RSA* new_key = RSA_new();
- BIGNUM* n = NULL;
- BIGNUM* e = NULL;
- if (!new_key) {
- goto cleanup;
- }
-
- // Check |size| is large enough and the modulus size is correct.
- if (size < sizeof(RSAPublicKey)) {
- goto cleanup;
- }
- if (key_struct->modulus_size_words != ANDROID_PUBKEY_MODULUS_SIZE_WORDS) {
- goto cleanup;
- }
-
- // Convert the modulus to big-endian byte order as expected by BN_bin2bn.
- n = BN_le2bn(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, NULL);
- if (!n) {
- goto cleanup;
- }
-
- // Read the exponent.
- e = BN_new();
- if (!e || !BN_set_word(e, key_struct->exponent)) {
- goto cleanup;
- }
-
- if (!RSA_set0_key(new_key, n, e, NULL)) {
- goto cleanup;
- }
- // RSA_set0_key takes ownership of its inputs on success.
- n = NULL;
- e = NULL;
-
- // Note that we don't extract the montgomery parameters n0inv and rr from
- // the RSAPublicKey structure. They assume a word size of 32 bits, but
- // BoringSSL may use a word size of 64 bits internally, so we're lacking the
- // top 32 bits of n0inv in general. For now, we just ignore the parameters
- // and have BoringSSL recompute them internally. More sophisticated logic can
- // be added here if/when we want the additional speedup from using the
- // pre-computed montgomery parameters.
-
- *key = new_key;
- new_key = NULL;
- ret = true;
-
-cleanup:
- RSA_free(new_key);
- BN_free(n);
- BN_free(e);
- return ret;
-}
-
-bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
- RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
- bool ret = false;
- BN_CTX* ctx = BN_CTX_new();
- BIGNUM* r32 = BN_new();
- BIGNUM* n0inv = BN_new();
- BIGNUM* rr = BN_new();
-
- if (sizeof(RSAPublicKey) > size || RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
- goto cleanup;
- }
-
- // Store the modulus size.
- key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
-
- // Compute and store n0inv = -1 / N[0] mod 2^32.
- if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) || !BN_mod(n0inv, RSA_get0_n(key), r32, ctx) ||
- !BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
- goto cleanup;
- }
- key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
-
- // Store the modulus.
- if (!BN_bn2le_padded(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, RSA_get0_n(key))) {
- goto cleanup;
- }
-
- // Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
- if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
- !BN_mod_sqr(rr, rr, RSA_get0_n(key), ctx) ||
- !BN_bn2le_padded(key_struct->rr, ANDROID_PUBKEY_MODULUS_SIZE, rr)) {
- goto cleanup;
- }
-
- // Store the exponent.
- key_struct->exponent = (uint32_t)BN_get_word(RSA_get0_e(key));
-
- ret = true;
-
-cleanup:
- BN_free(rr);
- BN_free(n0inv);
- BN_free(r32);
- BN_CTX_free(ctx);
- return ret;
-}
diff --git a/libcrypto_utils/tests/Android.bp b/libcrypto_utils/tests/Android.bp
index 087e83b27..5aadfe203 100644
--- a/libcrypto_utils/tests/Android.bp
+++ b/libcrypto_utils/tests/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_test_host {
name: "libcrypto_utils_test",
srcs: ["android_pubkey_test.cpp"],
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 68b21c6a0..24110eeae 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -1,58 +1,44 @@
-package {
- default_applicable_licenses: ["system_core_libcutils_license"],
-}
-
-license {
- name: "system_core_libcutils_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-BSD",
- "SPDX-license-identifier-MIT", // strlcpy.c
- ],
- license_text: [
- "NOTICE",
- ],
-}
+//
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
-filegroup {
- name: "android_filesystem_config_header",
- srcs: ["include/private/android_filesystem_config.h"],
-}
-
-cc_defaults {
- name: "libcutils_defaults",
- cflags: [
- "-Wno-exit-time-destructors",
- ],
+// some files must not be compiled when building against Mingw
+// they correspond to features not used by our host development tools
+// which are also hard or even impossible to port to native Win32
+libcutils_nonwindows_sources = [
+ "fs.cpp",
+ "hashmap.cpp",
+ "multiuser.cpp",
+ "str_parms.cpp",
+]
- product_available: true,
- ramdisk_available: true,
- recovery_available: true,
+cc_library_headers {
+ name: "libcutils_headers",
vendor_available: true,
- vendor_ramdisk_available: true,
-
+ recovery_available: true,
host_supported: true,
- native_bridge_supported: true,
-
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
min_sdk_version: "29",
-}
-
-cc_library_headers {
- name: "libcutils_headers",
- defaults: ["libcutils_defaults"],
-
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
vendor: {
- override_export_include_dirs: ["include_outside_system"],
- },
- product: {
- override_export_include_dirs: ["include_outside_system"],
+ override_export_include_dirs: ["include_vndk"],
},
linux_bionic: {
enabled: true,
@@ -66,7 +52,15 @@ cc_library_headers {
// Socket specific parts of libcutils that are safe to statically link into an APEX.
cc_library {
name: "libcutils_sockets",
- defaults: ["libcutils_defaults"],
+ vendor_available: true,
+ recovery_available: true,
+ host_supported: true,
+ native_bridge_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
export_include_dirs: ["include"],
@@ -137,30 +131,27 @@ cc_test {
},
}
-// some files must not be compiled when building against Mingw
-// they correspond to features not used by our host development tools
-// which are also hard or even impossible to port to native Win32
-libcutils_nonwindows_sources = [
- "fs.cpp",
- "hashmap.cpp",
- "multiuser.cpp",
- "str_parms.cpp",
-]
-
cc_library {
name: "libcutils",
- defaults: ["libcutils_defaults"],
+ vendor_available: true,
vndk: {
enabled: true,
support_system_process: true,
},
+ recovery_available: true,
+ host_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
+ native_bridge_supported: true,
srcs: [
"config_utils.cpp",
"canned_fs_config.cpp",
"iosched_policy.cpp",
"load_file.cpp",
"native_handle.cpp",
- "properties.cpp",
"record_stream.cpp",
"strlcpy.c",
"threads.cpp",
@@ -196,6 +187,7 @@ cc_library {
"fs_config.cpp",
"klog.cpp",
"partition_utils.cpp",
+ "properties.cpp",
"qtaguid.cpp",
"trace-dev.cpp",
"uevent.cpp",
@@ -228,19 +220,14 @@ cc_library {
},
},
- // qtaguid.cpp loads libnetd_client.so with dlopen(). Since
- // the interface of libnetd_client.so may vary between AOSP
- // releases, exclude qtaguid.cpp from the VNDK-SP variant.
vendor: {
exclude_srcs: [
+ // qtaguid.cpp loads libnetd_client.so with dlopen(). Since
+ // the interface of libnetd_client.so may vary between AOSP
+ // releases, exclude qtaguid.cpp from the VNDK-SP variant.
"qtaguid.cpp",
],
- },
- product: {
- exclude_srcs: [
- "qtaguid.cpp",
- ],
- },
+ }
},
whole_static_libs: ["libcutils_sockets"],
@@ -251,6 +238,7 @@ cc_library {
header_libs: [
"libbase_headers",
"libcutils_headers",
+ "libutils_headers",
"libprocessgroup_headers",
],
export_header_lib_headers: [
@@ -270,7 +258,6 @@ cc_defaults {
name: "libcutils_test_default",
srcs: [
"native_handle_test.cpp",
- "properties_test.cpp",
"sockets_test.cpp",
],
@@ -282,6 +269,7 @@ cc_defaults {
"ashmem_test.cpp",
"fs_config_test.cpp",
"multiuser_test.cpp",
+ "properties_test.cpp",
"sched_policy_test.cpp",
"str_parms_test.cpp",
"trace-dev_test.cpp",
@@ -354,18 +342,3 @@ cc_test {
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
-
-rust_bindgen {
- name: "libcutils_bindgen",
- wrapper_src: "rust/cutils.h",
- crate_name: "cutils_bindgen",
- source_stem: "bindings",
- local_include_dirs: ["include"],
- bindgen_flags: [
- "--allowlist-function", "multiuser_get_app_id",
- "--allowlist-function", "multiuser_get_uid",
- "--allowlist-function", "multiuser_get_user_id",
- "--allowlist-var", "AID_KEYSTORE",
- "--allowlist-var", "AID_USER_OFFSET",
- ],
-}
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
index 7529cb920..c18ed514b 100644
--- a/libcutils/OWNERS
+++ b/libcutils/OWNERS
@@ -1 +1,4 @@
-include platform/system/core:/janitors/OWNERS
+cferris@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 6a27f9a20..8c232f0cd 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -122,8 +122,7 @@ static bool check_vendor_memfd_allowed() {
return true;
}
- // Non-numeric should be a single ASCII character. Characters after the
- // first are ignored.
+ /* If its not a number, assume string, but check if its a sane string */
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
vndk_version.c_str());
@@ -159,11 +158,9 @@ static bool __has_memfd_support() {
return false;
}
- // Check if kernel support exists, otherwise fall back to ashmem.
- // This code needs to build on old API levels, so we can't use the libc
- // wrapper.
+ /* Check if kernel support exists, otherwise fall back to ashmem */
android::base::unique_fd fd(
- syscall(__NR_memfd_create, "test_android_memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING));
+ syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
if (fd == -1) {
ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
return false;
@@ -214,16 +211,13 @@ static int __ashmem_open_locked()
// fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
if (fd < 0) {
- int saved_errno = errno;
fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
- if (fd < 0) {
- /* Q launching devices and newer must not reach here since they should have been
- * able to open ashmem_device_path */
- ALOGE("Unable to open ashmem device %s (error = %s) and /dev/ashmem(error = %s)",
- ashmem_device_path.c_str(), strerror(saved_errno), strerror(errno));
- return fd;
- }
}
+
+ if (fd < 0) {
+ return fd;
+ }
+
struct stat st;
int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
if (ret < 0) {
@@ -335,9 +329,7 @@ int ashmem_valid(int fd)
}
static int memfd_create_region(const char* name, size_t size) {
- // This code needs to build on old API levels, so we can't use the libc
- // wrapper.
- android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING));
+ android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
if (fd == -1) {
ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
diff --git a/libcutils/ashmem_test.cpp b/libcutils/ashmem_test.cpp
index fb657f6e6..b37d020fb 100644
--- a/libcutils/ashmem_test.cpp
+++ b/libcutils/ashmem_test.cpp
@@ -35,11 +35,6 @@ void TestCreateRegion(size_t size, unique_fd &fd, int prot) {
ASSERT_TRUE(ashmem_valid(fd));
ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));
ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
-
- // We've been inconsistent historically about whether or not these file
- // descriptors were CLOEXEC. Make sure we're consistent going forward.
- // https://issuetracker.google.com/165667331
- ASSERT_EQ(FD_CLOEXEC, (fcntl(fd, F_GETFD) & FD_CLOEXEC));
}
void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index e9497a806..5805a4d19 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -35,9 +35,9 @@
#include <string>
#include <android-base/strings.h>
-#include <cutils/fs.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include <utils/Compat.h>
#include "fs_config.h"
@@ -80,18 +80,16 @@ static const struct fs_path_config android_dirs[] = {
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
{ 00751, AID_ROOT, AID_SHELL, 0, "product/bin" },
- { 00751, AID_ROOT, AID_SHELL, 0, "product/apex/*/bin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
- { 00750, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/xbin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
- { 00751, AID_ROOT, AID_SHELL, 0, "vendor/apex/*/bin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
// clang-format on
@@ -203,28 +201,20 @@ static const struct fs_path_config android_files[] = {
CAP_MASK_LONG(CAP_SETGID),
"system/bin/simpleperf_app_runner" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" },
-#ifdef __LP64__
- { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/linker64" },
-#else
- { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/linker" },
-#endif
- { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
- { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "odm/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "product/apex/*bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system_ext/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "vendor/apex/*bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
// clang-format on
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index 1913c1e4e..d80caa698 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -1,20 +1,14 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+/* cutils/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed. It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
*/
-#pragma once
+#ifndef _CUTILS_ASHMEM_H
+#define _CUTILS_ASHMEM_H
#include <stddef.h>
@@ -36,3 +30,5 @@ int ashmem_get_size_region(int fd);
#ifdef __cplusplus
}
#endif
+
+#endif /* _CUTILS_ASHMEM_H */
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index 78d8bc654..d2e08712a 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -14,30 +14,27 @@
* limitations under the License.
*/
-#pragma once
+#ifndef __CUTILS_PROPERTIES_H
+#define __CUTILS_PROPERTIES_H
#include <sys/cdefs.h>
#include <stddef.h>
-#include <stdint.h>
-
-#if __has_include(<sys/system_properties.h>)
#include <sys/system_properties.h>
-#else
-#define PROP_VALUE_MAX 92
-#endif
+#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
-//
-// Deprecated.
-//
-// See <android-base/properties.h> for better API.
-//
-
-#define PROPERTY_KEY_MAX PROP_NAME_MAX
-#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
+/* System properties are *small* name value pairs managed by the
+** property service. If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+** these, but with different names. (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX PROP_NAME_MAX
+#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
/* property_get: returns the length of the value which will never be
** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
@@ -149,3 +146,5 @@ int property_get(const char *key, char *value, const char *default_value) {
#ifdef __cplusplus
}
#endif
+
+#endif
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 0082c6c63..ba4846e33 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#pragma once
+#ifndef _LIBS_CUTILS_THREADS_H
+#define _LIBS_CUTILS_THREADS_H
#include <sys/types.h>
-#if defined(_WIN32)
-#include <windows.h>
-#else
+#if !defined(_WIN32)
#include <pthread.h>
+#else
+#include <windows.h>
#endif
#ifdef __cplusplus
@@ -31,10 +32,46 @@ extern "C" {
//
// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
//
-#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
+
extern pid_t gettid();
-#endif
+
+//
+// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
+//
+
+#if !defined(_WIN32)
+
+typedef struct {
+ pthread_mutex_t lock;
+ int has_tls;
+ pthread_key_t tls;
+} thread_store_t;
+
+#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
+
+#else // !defined(_WIN32)
+
+typedef struct {
+ int lock_init;
+ int has_tls;
+ DWORD tls;
+ CRITICAL_SECTION lock;
+} thread_store_t;
+
+#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
+
+#endif // !defined(_WIN32)
+
+typedef void (*thread_store_destruct_t)(void* value);
+
+extern void* thread_store_get(thread_store_t* store);
+
+extern void thread_store_set(thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy);
#ifdef __cplusplus
}
#endif
+
+#endif /* _LIBS_CUTILS_THREADS_H */
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 24c6ae629..c74ee3e9d 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -103,6 +103,14 @@ void atrace_setup();
void atrace_update_tags();
/**
+ * Set whether the process is debuggable. By default the process is not
+ * considered debuggable. If the process is not debuggable then application-
+ * level tracing is not allowed unless the ro.debuggable system property is
+ * set to '1'.
+ */
+void atrace_set_debuggable(bool debuggable);
+
+/**
* Set whether tracing is enabled for the current process. This is used to
* prevent tracing within the Zygote process.
*/
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8f22d8983..b73a29b22 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -34,9 +34,16 @@
* partition, from which the system reads passwd and group files.
*/
-#pragma once
+#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
+#define _ANDROID_FILESYSTEM_CONFIG_H_
-/* This is the main Users and Groups config for the platform.
+#include <sys/types.h>
+
+#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+#include <private/fs_config.h>
+#endif
+
+/* This is the master Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/
@@ -127,9 +134,6 @@
#define AID_EXT_DATA_RW 1078 /* GID for app-private data directories on external storage */
#define AID_EXT_OBB_RW 1079 /* GID for OBB directories on external storage */
#define AID_CONTEXT_HUB 1080 /* GID for access to the Context Hub */
-#define AID_VIRTMANAGER 1081 /* VirtManager daemon */
-#define AID_ARTD 1082 /* ART Service daemon */
-#define AID_UWB 1083 /* UWB subsystem */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -220,3 +224,5 @@
* documented at the top of this header file.
* Also see build/tools/fs_config for more details.
*/
+
+#endif
diff --git a/libcutils/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
index ad4de4ce6..135b91c6c 100644
--- a/libcutils/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#pragma once
+#ifndef _CANNED_FS_CONFIG_H
+#define _CANNED_FS_CONFIG_H
#include <inttypes.h>
-#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -26,3 +26,5 @@ void canned_fs_config(const char* path, int dir, const char* target_out_path, un
unsigned* gid, unsigned* mode, uint64_t* capabilities);
__END_DECLS
+
+#endif
diff --git a/libcutils/include_outside_system/cutils/android_filesystem_config.h b/libcutils/include_vndk/cutils/android_filesystem_config.h
index 13a5a08ef..13a5a08ef 120000
--- a/libcutils/include_outside_system/cutils/android_filesystem_config.h
+++ b/libcutils/include_vndk/cutils/android_filesystem_config.h
diff --git a/libcutils/include_outside_system/cutils/android_get_control_file.h b/libcutils/include_vndk/cutils/android_get_control_file.h
index 70d6a3b0b..70d6a3b0b 120000
--- a/libcutils/include_outside_system/cutils/android_get_control_file.h
+++ b/libcutils/include_vndk/cutils/android_get_control_file.h
diff --git a/libcutils/include_outside_system/cutils/android_reboot.h b/libcutils/include_vndk/cutils/android_reboot.h
index 9e1bf4cc4..9e1bf4cc4 120000
--- a/libcutils/include_outside_system/cutils/android_reboot.h
+++ b/libcutils/include_vndk/cutils/android_reboot.h
diff --git a/libcutils/include_outside_system/cutils/ashmem.h b/libcutils/include_vndk/cutils/ashmem.h
index 5c07beb06..5c07beb06 120000
--- a/libcutils/include_outside_system/cutils/ashmem.h
+++ b/libcutils/include_vndk/cutils/ashmem.h
diff --git a/libcutils/include_outside_system/cutils/atomic.h b/libcutils/include_vndk/cutils/atomic.h
index f4f14fe7d..f4f14fe7d 120000
--- a/libcutils/include_outside_system/cutils/atomic.h
+++ b/libcutils/include_vndk/cutils/atomic.h
diff --git a/libcutils/include_outside_system/cutils/bitops.h b/libcutils/include_vndk/cutils/bitops.h
index edbd60c04..edbd60c04 120000
--- a/libcutils/include_outside_system/cutils/bitops.h
+++ b/libcutils/include_vndk/cutils/bitops.h
diff --git a/libcutils/include_outside_system/cutils/compiler.h b/libcutils/include_vndk/cutils/compiler.h
index 08ebc103e..08ebc103e 120000
--- a/libcutils/include_outside_system/cutils/compiler.h
+++ b/libcutils/include_vndk/cutils/compiler.h
diff --git a/libcutils/include_outside_system/cutils/config_utils.h b/libcutils/include_vndk/cutils/config_utils.h
index e011738d7..e011738d7 120000
--- a/libcutils/include_outside_system/cutils/config_utils.h
+++ b/libcutils/include_vndk/cutils/config_utils.h
diff --git a/libcutils/include_outside_system/cutils/fs.h b/libcutils/include_vndk/cutils/fs.h
index 576bfa3a4..576bfa3a4 120000
--- a/libcutils/include_outside_system/cutils/fs.h
+++ b/libcutils/include_vndk/cutils/fs.h
diff --git a/libcutils/include_outside_system/cutils/hashmap.h b/libcutils/include_vndk/cutils/hashmap.h
index 6b18406ae..6b18406ae 120000
--- a/libcutils/include_outside_system/cutils/hashmap.h
+++ b/libcutils/include_vndk/cutils/hashmap.h
diff --git a/libcutils/include_outside_system/cutils/iosched_policy.h b/libcutils/include_vndk/cutils/iosched_policy.h
index 26cf33323..26cf33323 120000
--- a/libcutils/include_outside_system/cutils/iosched_policy.h
+++ b/libcutils/include_vndk/cutils/iosched_policy.h
diff --git a/libcutils/include_outside_system/cutils/klog.h b/libcutils/include_vndk/cutils/klog.h
index 8ca85ff65..8ca85ff65 120000
--- a/libcutils/include_outside_system/cutils/klog.h
+++ b/libcutils/include_vndk/cutils/klog.h
diff --git a/libcutils/include_outside_system/cutils/list.h b/libcutils/include_vndk/cutils/list.h
index 9fa4c90f3..9fa4c90f3 120000
--- a/libcutils/include_outside_system/cutils/list.h
+++ b/libcutils/include_vndk/cutils/list.h
diff --git a/libcutils/include_outside_system/cutils/log.h b/libcutils/include_vndk/cutils/log.h
index b868d5080..b868d5080 120000
--- a/libcutils/include_outside_system/cutils/log.h
+++ b/libcutils/include_vndk/cutils/log.h
diff --git a/libcutils/include_outside_system/cutils/memory.h b/libcutils/include_vndk/cutils/memory.h
index e0e7abcb6..e0e7abcb6 120000
--- a/libcutils/include_outside_system/cutils/memory.h
+++ b/libcutils/include_vndk/cutils/memory.h
diff --git a/libcutils/include_outside_system/cutils/misc.h b/libcutils/include_vndk/cutils/misc.h
index db09eb59e..db09eb59e 120000
--- a/libcutils/include_outside_system/cutils/misc.h
+++ b/libcutils/include_vndk/cutils/misc.h
diff --git a/libcutils/include_outside_system/cutils/multiuser.h b/libcutils/include_vndk/cutils/multiuser.h
index 524111f1c..524111f1c 120000
--- a/libcutils/include_outside_system/cutils/multiuser.h
+++ b/libcutils/include_vndk/cutils/multiuser.h
diff --git a/libcutils/include_outside_system/cutils/native_handle.h b/libcutils/include_vndk/cutils/native_handle.h
index e324d4e75..e324d4e75 120000
--- a/libcutils/include_outside_system/cutils/native_handle.h
+++ b/libcutils/include_vndk/cutils/native_handle.h
diff --git a/libcutils/include_outside_system/cutils/partition_utils.h b/libcutils/include_vndk/cutils/partition_utils.h
index d9734c8ef..d9734c8ef 120000
--- a/libcutils/include_outside_system/cutils/partition_utils.h
+++ b/libcutils/include_vndk/cutils/partition_utils.h
diff --git a/libcutils/include_outside_system/cutils/properties.h b/libcutils/include_vndk/cutils/properties.h
index d56118e67..d56118e67 120000
--- a/libcutils/include_outside_system/cutils/properties.h
+++ b/libcutils/include_vndk/cutils/properties.h
diff --git a/libcutils/include_outside_system/cutils/qtaguid.h b/libcutils/include_vndk/cutils/qtaguid.h
index bc02441a6..bc02441a6 120000
--- a/libcutils/include_outside_system/cutils/qtaguid.h
+++ b/libcutils/include_vndk/cutils/qtaguid.h
diff --git a/libcutils/include_outside_system/cutils/record_stream.h b/libcutils/include_vndk/cutils/record_stream.h
index 8de649456..8de649456 120000
--- a/libcutils/include_outside_system/cutils/record_stream.h
+++ b/libcutils/include_vndk/cutils/record_stream.h
diff --git a/libcutils/include_outside_system/cutils/sched_policy.h b/libcutils/include_vndk/cutils/sched_policy.h
index ddebdd0f6..ddebdd0f6 120000
--- a/libcutils/include_outside_system/cutils/sched_policy.h
+++ b/libcutils/include_vndk/cutils/sched_policy.h
diff --git a/libcutils/include_outside_system/cutils/sockets.h b/libcutils/include_vndk/cutils/sockets.h
index 585250c31..585250c31 120000
--- a/libcutils/include_outside_system/cutils/sockets.h
+++ b/libcutils/include_vndk/cutils/sockets.h
diff --git a/libcutils/include_outside_system/cutils/str_parms.h b/libcutils/include_vndk/cutils/str_parms.h
index 9c79a8f88..9c79a8f88 120000
--- a/libcutils/include_outside_system/cutils/str_parms.h
+++ b/libcutils/include_vndk/cutils/str_parms.h
diff --git a/libcutils/include_outside_system/cutils/threads.h b/libcutils/include_vndk/cutils/threads.h
index 99330ffad..99330ffad 120000
--- a/libcutils/include_outside_system/cutils/threads.h
+++ b/libcutils/include_vndk/cutils/threads.h
diff --git a/libcutils/include_outside_system/cutils/trace.h b/libcutils/include_vndk/cutils/trace.h
index b12e14073..b12e14073 120000
--- a/libcutils/include_outside_system/cutils/trace.h
+++ b/libcutils/include_vndk/cutils/trace.h
diff --git a/libcutils/include_outside_system/cutils/uevent.h b/libcutils/include_vndk/cutils/uevent.h
index 451283a0f..451283a0f 120000
--- a/libcutils/include_outside_system/cutils/uevent.h
+++ b/libcutils/include_vndk/cutils/uevent.h
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 03f04961c..5dbbeba48 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -16,19 +16,27 @@
#include <cutils/properties.h>
+#define LOG_TAG "properties"
+// #define LOG_NDEBUG 0
+
+#include <assert.h>
+#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <android-base/properties.h>
+#include <cutils/sockets.h>
+#include <log/log.h>
-int8_t property_get_bool(const char* key, int8_t default_value) {
- if (!key) return default_value;
+int8_t property_get_bool(const char *key, int8_t default_value) {
+ if (!key) {
+ return default_value;
+ }
int8_t result = default_value;
- char buf[PROPERTY_VALUE_MAX] = {};
+ char buf[PROPERTY_VALUE_MAX] = {'\0'};
int len = property_get(key, buf, "");
if (len == 1) {
@@ -49,53 +57,73 @@ int8_t property_get_bool(const char* key, int8_t default_value) {
return result;
}
-template <typename T>
-static T property_get_int(const char* key, T default_value) {
- if (!key) return default_value;
-
- char value[PROPERTY_VALUE_MAX] = {};
- if (property_get(key, value, "") < 1) return default_value;
-
- // libcutils unwisely allows octal, which libbase doesn't.
- T result = default_value;
- int saved_errno = errno;
- errno = 0;
- char* end = nullptr;
- intmax_t v = strtoimax(value, &end, 0);
- if (errno != ERANGE && end != value && v >= std::numeric_limits<T>::min() &&
- v <= std::numeric_limits<T>::max()) {
- result = v;
+// Convert string property to int (default if fails); return default value if out of bounds
+static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
+ intmax_t default_value) {
+ if (!key) {
+ return default_value;
}
- errno = saved_errno;
+
+ intmax_t result = default_value;
+ char buf[PROPERTY_VALUE_MAX] = {'\0'};
+ char *end = NULL;
+
+ int len = property_get(key, buf, "");
+ if (len > 0) {
+ int tmp = errno;
+ errno = 0;
+
+ // Infer base automatically
+ result = strtoimax(buf, &end, /*base*/ 0);
+ if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
+ // Over or underflow
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - overflow", __FUNCTION__, key, default_value);
+ } else if (result < lower_bound || result > upper_bound) {
+ // Out of range of requested bounds
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - out of range", __FUNCTION__, key, default_value);
+ } else if (end == buf) {
+ // Numeric conversion failed
+ result = default_value;
+ ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", __FUNCTION__, key,
+ default_value);
+ }
+
+ errno = tmp;
+ }
+
return result;
}
-int64_t property_get_int64(const char* key, int64_t default_value) {
- return property_get_int<int64_t>(key, default_value);
+int64_t property_get_int64(const char *key, int64_t default_value) {
+ return (int64_t)property_get_imax(key, INT64_MIN, INT64_MAX, default_value);
}
-int32_t property_get_int32(const char* key, int32_t default_value) {
- return property_get_int<int32_t>(key, default_value);
+int32_t property_get_int32(const char *key, int32_t default_value) {
+ return (int32_t)property_get_imax(key, INT32_MIN, INT32_MAX, default_value);
}
-int property_set(const char* key, const char* value) {
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+int property_set(const char *key, const char *value) {
return __system_property_set(key, value);
}
-int property_get(const char* key, char* value, const char* default_value) {
+int property_get(const char *key, char *value, const char *default_value) {
int len = __system_property_get(key, value);
- if (len < 1 && default_value) {
- snprintf(value, PROPERTY_VALUE_MAX, "%s", default_value);
- return strlen(value);
+ if (len > 0) {
+ return len;
+ }
+ if (default_value) {
+ len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
+ memcpy(value, default_value, len);
+ value[len] = '\0';
}
return len;
}
-#if __has_include(<sys/system_properties.h>)
-
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
struct callback_data {
void (*callback)(const char* name, const char* value, void* cookie);
void* cookie;
@@ -111,8 +139,6 @@ static void property_list_callback(const prop_info* pi, void* data) {
}
int property_list(void (*fn)(const char* name, const char* value, void* cookie), void* cookie) {
- callback_data data = {fn, cookie};
+ callback_data data = { fn, cookie };
return __system_property_foreach(property_list_callback, &data);
}
-
-#endif
diff --git a/libcutils/properties_test.cpp b/libcutils/properties_test.cpp
index efc01833a..79219720e 100644
--- a/libcutils/properties_test.cpp
+++ b/libcutils/properties_test.cpp
@@ -93,179 +93,160 @@ protected:
}
};
-TEST_F(PropertiesTest, property_set_null_key) {
+TEST_F(PropertiesTest, SetString) {
+
// Null key -> unsuccessful set
- EXPECT_GT(0, property_set(/*key*/ NULL, PROPERTY_TEST_VALUE_DEFAULT));
-}
+ {
+ // Null key -> fails
+ EXPECT_GT(0, property_set(/*key*/NULL, PROPERTY_TEST_VALUE_DEFAULT));
+ }
-TEST_F(PropertiesTest, property_set_null_value) {
- // Null value -> OK, and it clears the value
- EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/ NULL));
- ResetValue();
+ // Null value -> returns default value
+ {
+ // Null value -> OK , and it clears the value
+ EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));
+ ResetValue();
- // Since the value is null, default value will be returned
- size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
- EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
- EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
-}
+ // Since the value is null, default value will be returned
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
+ EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
+ }
-TEST_F(PropertiesTest, property_set) {
// Trivial case => get returns what was set
- size_t len = SetAndGetProperty("hello_world");
- EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
- EXPECT_STREQ("hello_world", mValue);
- ResetValue();
-}
+ {
+ size_t len = SetAndGetProperty("hello_world");
+ EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
+ EXPECT_STREQ("hello_world", mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_set_empty) {
// Set to empty string => get returns default always
- const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
- size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
- EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
- EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
- ResetValue();
-}
+ {
+ const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
+ size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
+ EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
+ EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_set_max_length) {
// Set to max length => get returns what was set
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
+ {
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
- int len = SetAndGetProperty(maxLengthString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len) << "max length key";
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
-}
+ int len = SetAndGetProperty(maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX-1, len) << "max length key";
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_set_too_long) {
// Set to max length + 1 => set fails
- const char* VALID_TEST_VALUE = "VALID_VALUE";
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
+ {
+ const char* VALID_TEST_VALUE = "VALID_VALUE";
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));
- std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
- // Expect that the value set fails since it's too long
- EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
- size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ // Expect that the value set fails since it's too long
+ EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
- EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
- EXPECT_STREQ(VALID_TEST_VALUE, mValue);
- ResetValue();
+ EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
+ EXPECT_STREQ(VALID_TEST_VALUE, mValue);
+ ResetValue();
+ }
}
-TEST_F(PropertiesTest, property_get_too_long) {
+TEST_F(PropertiesTest, GetString) {
+
// Try to use a default value that's too long => get truncates the value
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
- std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
+ std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
- // Expect that the value is truncated since it's too long (by 1)
- int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
-}
+ // Expect that the value is truncated since it's too long (by 1)
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_get_default_too_long) {
// Try to use a default value that's the max length => get succeeds
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
+ std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
- // Expect that the value matches maxLengthString
- int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
- EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
- EXPECT_STREQ(maxLengthString.c_str(), mValue);
- ResetValue();
-}
+ // Expect that the value matches maxLengthString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
+ EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+ EXPECT_STREQ(maxLengthString.c_str(), mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_get_default_okay) {
// Try to use a default value of length one => get succeeds
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string oneCharString = std::string(1, 'c');
+ std::string oneCharString = std::string(1, 'c');
- // Expect that the value matches oneCharString
- int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
- EXPECT_EQ(1, len);
- EXPECT_STREQ(oneCharString.c_str(), mValue);
- ResetValue();
-}
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
+ EXPECT_EQ(1, len);
+ EXPECT_STREQ(oneCharString.c_str(), mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_get_default_empty) {
// Try to use a default value of length zero => get succeeds
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- std::string zeroCharString = std::string(0, 'd');
+ std::string zeroCharString = std::string(0, 'd');
- // Expect that the value matches oneCharString
- int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
- EXPECT_EQ(0, len);
- EXPECT_STREQ(zeroCharString.c_str(), mValue);
- ResetValue();
-}
+ // Expect that the value matches oneCharString
+ int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
+ EXPECT_EQ(0, len);
+ EXPECT_STREQ(zeroCharString.c_str(), mValue);
+ ResetValue();
+ }
-TEST_F(PropertiesTest, property_get_default_NULL) {
// Try to use a NULL default value => get returns 0
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+ {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
- // Expect a return value of 0
- int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
- EXPECT_EQ(0, len);
- ResetValue();
-}
-
-TEST_F(PropertiesTest, property_get_bool_0) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "0"));
- ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
-}
-
-TEST_F(PropertiesTest, property_get_bool_1) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "1"));
- ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
-}
-
-TEST_F(PropertiesTest, property_get_bool_false) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "false"));
- ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
-}
-
-TEST_F(PropertiesTest, property_get_bool_n) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "n"));
- ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
-}
-
-TEST_F(PropertiesTest, property_get_bool_no) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "no"));
- ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
-}
-
-TEST_F(PropertiesTest, property_get_bool_off) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "off"));
- ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));
-}
-
-TEST_F(PropertiesTest, property_get_bool_on) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "on"));
- ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
-}
-
-TEST_F(PropertiesTest, property_get_bool_true) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "true"));
- ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
+ // Expect a return value of 0
+ int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
+ EXPECT_EQ(0, len);
+ ResetValue();
+ }
}
-TEST_F(PropertiesTest, property_get_bool_y) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "y"));
- ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
-}
+TEST_F(PropertiesTest, GetBool) {
+ /**
+ * TRUE
+ */
+ const char *valuesTrue[] = { "1", "true", "y", "yes", "on", };
+ for (size_t i = 0; i < arraysize(valuesTrue); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesTrue[i]));
+ bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);
+ EXPECT_TRUE(val) << "Property should've been TRUE for value: '" << valuesTrue[i] << "'";
+ }
-TEST_F(PropertiesTest, property_get_bool_yes) {
- ASSERT_OK(property_set(PROPERTY_TEST_KEY, "yes"));
- ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));
-}
+ /**
+ * FALSE
+ */
+ const char *valuesFalse[] = { "0", "false", "n", "no", "off", };
+ for (size_t i = 0; i < arraysize(valuesFalse); ++i) {
+ ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesFalse[i]));
+ bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);
+ EXPECT_FALSE(val) << "Property shoud've been FALSE For string value: '" << valuesFalse[i] << "'";
+ }
-TEST_F(PropertiesTest, property_get_bool_neither) {
+ /**
+ * NEITHER
+ */
const char *valuesNeither[] = { "x0", "x1", "2", "-2", "True", "False", "garbage", "", " ",
"+1", " 1 ", " true", " true ", " y ", " yes", "yes ",
"+0", "-0", "00", " 00 ", " false", "false ",
@@ -282,7 +263,7 @@ TEST_F(PropertiesTest, property_get_bool_neither) {
}
}
-TEST_F(PropertiesTest, property_get_int64) {
+TEST_F(PropertiesTest, GetInt64) {
const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD);
const std::string longMaxString = ToString(INT64_MAX);
@@ -329,7 +310,7 @@ TEST_F(PropertiesTest, property_get_int64) {
}
}
-TEST_F(PropertiesTest, property_get_int32) {
+TEST_F(PropertiesTest, GetInt32) {
const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF);
const std::string intMaxString = ToString(INT32_MAX);
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index 2fe877c4e..b94d134be 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -38,24 +38,24 @@ class netdHandler {
int (*netdDeleteTagData)(uint32_t, uid_t);
};
-int stubTagSocket(int, uint32_t, uid_t) {
+int dummyTagSocket(int, uint32_t, uid_t) {
return -EREMOTEIO;
}
-int stubUntagSocket(int) {
+int dummyUntagSocket(int) {
return -EREMOTEIO;
}
-int stubSetCounterSet(uint32_t, uid_t) {
+int dummySetCounterSet(uint32_t, uid_t) {
return -EREMOTEIO;
}
-int stubDeleteTagData(uint32_t, uid_t) {
+int dummyDeleteTagData(uint32_t, uid_t) {
return -EREMOTEIO;
}
netdHandler initHandler(void) {
- netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
+ netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
diff --git a/libcutils/rust/cutils.h b/libcutils/rust/cutils.h
deleted file mode 100644
index 9b78af631..000000000
--- a/libcutils/rust/cutils.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#include <cutils/multiuser.h>
-#include <private/android_filesystem_config.h>
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index 6ece7a3af..a7e6b2d8e 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -16,19 +16,23 @@
#include <cutils/threads.h>
+// For gettid.
#if defined(__APPLE__)
+#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
#include <stdint.h>
-#elif defined(__linux__)
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
#include <syscall.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#endif
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
// No definition needed for Android because we'll just pick up bionic's copy.
-// No definition needed for Glibc >= 2.32 because it exposes its own copy.
-#else
+#ifndef __ANDROID__
pid_t gettid() {
#if defined(__APPLE__)
uint64_t tid;
@@ -40,4 +44,68 @@ pid_t gettid() {
return GetCurrentThreadId();
#endif
}
-#endif
+#endif // __ANDROID__
+
+#if !defined(_WIN32)
+
+void* thread_store_get( thread_store_t* store )
+{
+ if (!store->has_tls)
+ return NULL;
+
+ return pthread_getspecific( store->tls );
+}
+
+extern void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy)
+{
+ pthread_mutex_lock( &store->lock );
+ if (!store->has_tls) {
+ if (pthread_key_create( &store->tls, destroy) != 0) {
+ pthread_mutex_unlock(&store->lock);
+ return;
+ }
+ store->has_tls = 1;
+ }
+ pthread_mutex_unlock( &store->lock );
+
+ pthread_setspecific( store->tls, value );
+}
+
+#else /* !defined(_WIN32) */
+void* thread_store_get( thread_store_t* store )
+{
+ if (!store->has_tls)
+ return NULL;
+
+ return (void*) TlsGetValue( store->tls );
+}
+
+void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t /*destroy*/ )
+{
+ /* XXX: can't use destructor on thread exit */
+ if (!store->lock_init) {
+ store->lock_init = -1;
+ InitializeCriticalSection( &store->lock );
+ store->lock_init = -2;
+ } else while (store->lock_init != -2) {
+ Sleep(10); /* 10ms */
+ }
+
+ EnterCriticalSection( &store->lock );
+ if (!store->has_tls) {
+ store->tls = TlsAlloc();
+ if (store->tls == TLS_OUT_OF_INDEXES) {
+ LeaveCriticalSection( &store->lock );
+ return;
+ }
+ store->has_tls = 1;
+ }
+ LeaveCriticalSection( &store->lock );
+
+ TlsSetValue( store->tls, value );
+}
+#endif /* !defined(_WIN32) */
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 1ab63dc2f..5a09a2d39 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -30,9 +30,9 @@ void atrace_set_tracing_enabled(bool enabled)
static void atrace_init_once()
{
- atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
if (atrace_marker_fd == -1) {
- atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
}
if (atrace_marker_fd == -1) {
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 3b459e094..654342649 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -21,7 +21,6 @@
#include <errno.h>
#include <fcntl.h>
-#include <fnmatch.h>
#include <limits.h>
#include <pthread.h>
#include <stdatomic.h>
@@ -52,6 +51,7 @@ constexpr uint32_t kSeqNoNotInit = static_cast<uint32_t>(-1);
atomic_bool atrace_is_ready = ATOMIC_VAR_INIT(false);
int atrace_marker_fd = -1;
uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+static bool atrace_is_debuggable = false;
static atomic_bool atrace_is_enabled = ATOMIC_VAR_INIT(true);
static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -95,6 +95,15 @@ uint64_t atrace_get_enabled_tags()
return atrace_enabled_tags;
}
+// Set whether this process is debuggable, which determines whether
+// application-level tracing is allowed when the ro.debuggable system property
+// is not set to '1'.
+void atrace_set_debuggable(bool debuggable)
+{
+ atrace_is_debuggable = debuggable;
+ atrace_update_tags();
+}
+
// Check whether the given command line matches one of the comma-separated
// values listed in the app_cmdlines property.
static bool atrace_is_cmdline_match(const char* cmdline)
@@ -107,7 +116,7 @@ static bool atrace_is_cmdline_match(const char* cmdline)
for (int i = 0; i < count; i++) {
snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
property_get(buf, value, "");
- if (fnmatch(value, cmdline, FNM_NOESCAPE) == 0) {
+ if (strcmp(value, "*") == 0 || strcmp(value, cmdline) == 0) {
return true;
}
}
@@ -118,21 +127,24 @@ static bool atrace_is_cmdline_match(const char* cmdline)
// Determine whether application-level tracing is enabled for this process.
static bool atrace_is_app_tracing_enabled()
{
+ bool sys_debuggable = property_get_bool("ro.debuggable", 0);
bool result = false;
- // Check whether tracing is enabled for this process.
- FILE * file = fopen("/proc/self/cmdline", "re");
- if (file) {
- char cmdline[4096];
- if (fgets(cmdline, sizeof(cmdline), file)) {
- result = atrace_is_cmdline_match(cmdline);
+ if (sys_debuggable || atrace_is_debuggable) {
+ // Check whether tracing is enabled for this process.
+ FILE * file = fopen("/proc/self/cmdline", "re");
+ if (file) {
+ char cmdline[4096];
+ if (fgets(cmdline, sizeof(cmdline), file)) {
+ result = atrace_is_cmdline_match(cmdline);
+ } else {
+ ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+ }
+ fclose(file);
} else {
- ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+ ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+ errno);
}
- fclose(file);
- } else {
- ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
- errno);
}
return result;
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 40bbd5c8f..bf244d2c7 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -101,7 +101,7 @@ int uevent_open_socket(int buf_sz, bool passcred) {
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- addr.nl_pid = 0;
+ addr.nl_pid = getpid();
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index a3d643e2c..b92f0863d 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libdiskconfig",
vendor_available: true,
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index f31b5f1a0..33ae13db6 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,23 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["system_core_libgrallocusage_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_core_libgrallocusage_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_library {
name: "libgrallocusage",
vendor_available: true,
diff --git a/libgrallocusage/OWNERS b/libgrallocusage/OWNERS
index de2bf16f4..154dc6d1b 100644
--- a/libgrallocusage/OWNERS
+++ b/libgrallocusage/OWNERS
@@ -1,2 +1,3 @@
-jreck@google.com
-lpy@google.com
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index 86f68fb57..b388e9555 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -1,16 +1,3 @@
-package {
- default_applicable_licenses: ["system_core_libkeyutils_license"],
-}
-
-license {
- name: "system_core_libkeyutils_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-BSD",
- ],
- // large-scale-change unable to identify any license_text files
-}
-
cc_library {
name: "libkeyutils",
cflags: ["-Werror"],
diff --git a/libkeyutils/keyutils_test.cpp b/libkeyutils/keyutils_test.cpp
index d03747b3e..d41c91b68 100644
--- a/libkeyutils/keyutils_test.cpp
+++ b/libkeyutils/keyutils_test.cpp
@@ -33,7 +33,7 @@
#include <gtest/gtest.h>
TEST(keyutils, smoke) {
- // Check that the exported type is the right size.
+ // Check that the exported type is sane.
ASSERT_EQ(4U, sizeof(key_serial_t));
// Check that all the functions actually exist.
diff --git a/libkeyutils/mini_keyctl/Android.bp b/libkeyutils/mini_keyctl/Android.bp
new file mode 100644
index 000000000..a04a3db24
--- /dev/null
+++ b/libkeyutils/mini_keyctl/Android.bp
@@ -0,0 +1,27 @@
+cc_library_static {
+ name: "libmini_keyctl_static",
+ srcs: [
+ "mini_keyctl_utils.cpp"
+ ],
+ shared_libs: [
+ "libbase",
+ "libkeyutils",
+ ],
+ cflags: ["-Werror", "-Wall", "-Wextra"],
+ export_include_dirs: ["."],
+}
+
+cc_binary {
+ name: "mini-keyctl",
+ srcs: [
+ "mini_keyctl.cpp",
+ ],
+ static_libs: [
+ "libmini_keyctl_static",
+ ],
+ shared_libs: [
+ "libbase",
+ "libkeyutils",
+ ],
+ cflags: ["-Werror", "-Wall", "-Wextra"],
+}
diff --git a/mini_keyctl/mini_keyctl.cpp b/libkeyutils/mini_keyctl/mini_keyctl.cpp
index 8aace9adb..8aace9adb 100644
--- a/mini_keyctl/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl/mini_keyctl.cpp
diff --git a/mini_keyctl/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
index fb9503f14..fb9503f14 100644
--- a/mini_keyctl/mini_keyctl_utils.cpp
+++ b/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
diff --git a/mini_keyctl/mini_keyctl_utils.h b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
index cc31d29ce..cc31d29ce 100644
--- a/mini_keyctl/mini_keyctl_utils.h
+++ b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
diff --git a/liblog/.clang-format b/liblog/.clang-format
new file mode 120000
index 000000000..fd0645fdf
--- /dev/null
+++ b/liblog/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2 \ No newline at end of file
diff --git a/liblog/Android.bp b/liblog/Android.bp
new file mode 100644
index 000000000..6051ac7fa
--- /dev/null
+++ b/liblog/Android.bp
@@ -0,0 +1,154 @@
+//
+// Copyright (C) 2008-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+liblog_sources = [
+ "log_event_list.cpp",
+ "log_event_write.cpp",
+ "logger_name.cpp",
+ "logger_read.cpp",
+ "logger_write.cpp",
+ "logprint.cpp",
+ "properties.cpp",
+]
+liblog_target_sources = [
+ "event_tag_map.cpp",
+ "log_time.cpp",
+ "pmsg_reader.cpp",
+ "pmsg_writer.cpp",
+ "logd_reader.cpp",
+ "logd_writer.cpp",
+]
+
+cc_library_headers {
+ name: "liblog_headers",
+ host_supported: true,
+ vendor_available: true,
+ ramdisk_available: true,
+ recovery_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
+ native_bridge_supported: true,
+ export_include_dirs: ["include"],
+ system_shared_libs: [],
+ stl: "none",
+ target: {
+ windows: {
+ enabled: true,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ vendor: {
+ override_export_include_dirs: ["include_vndk"],
+ },
+ },
+}
+
+// Shared and static library for host and device
+// ========================================================
+cc_library {
+ name: "liblog",
+ host_supported: true,
+ ramdisk_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ srcs: liblog_sources,
+
+ target: {
+ android: {
+ version_script: "liblog.map.txt",
+ srcs: liblog_target_sources,
+ // AddressSanitizer runtime library depends on liblog.
+ sanitize: {
+ address: false,
+ },
+ },
+ android_arm: {
+ // TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
+ ldflags: ["-Wl,--hash-style=both"],
+ },
+ windows: {
+ enabled: true,
+ },
+ not_windows: {
+ srcs: ["event_tag_map.cpp"],
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ },
+
+ header_libs: [
+ "libbase_headers",
+ "liblog_headers",
+ ],
+ export_header_lib_headers: ["liblog_headers"],
+
+ stubs: {
+ symbol_file: "liblog.map.txt",
+ versions: ["29", "30"],
+ },
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ // This is what we want to do:
+ // liblog_cflags := $(shell \
+ // sed -n \
+ // 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
+ // $(LOCAL_PATH)/event.logtags)
+ // so make sure we do not regret hard-coding it as follows:
+ "-DLIBLOG_LOG_TAG=1006",
+ "-DSNET_EVENT_LOG_TAG=1397638484",
+ ],
+ logtags: ["event.logtags"],
+ compile_multilib: "both",
+ apex_available: [
+ "//apex_available:platform",
+ // liblog is exceptionally available to the runtime APEX
+ // because the dynamic linker has to use it statically.
+ // See b/151051671
+ "com.android.runtime",
+ // DO NOT add more apex names here
+ ],
+}
+
+ndk_headers {
+ name: "liblog_ndk_headers",
+ from: "include/android",
+ to: "android",
+ srcs: ["include/android/log.h"],
+ license: "NOTICE",
+}
+
+ndk_library {
+ name: "liblog",
+ symbol_file: "liblog.map.txt",
+ first_version: "9",
+ unversioned_until: "current",
+}
+
+llndk_library {
+ name: "liblog",
+ native_bridge_supported: true,
+ symbol_file: "liblog.map.txt",
+ export_include_dirs: ["include_vndk"],
+}
diff --git a/liblog/NOTICE b/liblog/NOTICE
new file mode 100644
index 000000000..06a9081ca
--- /dev/null
+++ b/liblog/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2014, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/liblog/OWNERS b/liblog/OWNERS
new file mode 100644
index 000000000..babbe4ded
--- /dev/null
+++ b/liblog/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/liblog/README.md b/liblog/README.md
new file mode 100644
index 000000000..871399a57
--- /dev/null
+++ b/liblog/README.md
@@ -0,0 +1,166 @@
+Android liblog
+--------------
+
+Public Functions and Macros
+---------------------------
+
+ /*
+ * Please limit to 24 characters for runtime is loggable,
+ * 16 characters for persist is loggable, and logcat pretty
+ * alignment with limit of 7 characters.
+ */
+ #define LOG_TAG "yourtag"
+ #include <log/log.h>
+
+ ALOG(android_priority, tag, format, ...)
+ IF_ALOG(android_priority, tag)
+ LOG_PRI(priority, tag, format, ...)
+ LOG_PRI_VA(priority, tag, format, args)
+ #define LOG_TAG NULL
+ ALOGV(format, ...)
+ SLOGV(format, ...)
+ RLOGV(format, ...)
+ ALOGV_IF(cond, format, ...)
+ SLOGV_IF(cond, format, ...)
+ RLOGV_IF(cond, format, ...)
+ IF_ALOGC()
+ ALOGD(format, ...)
+ SLOGD(format, ...)
+ RLOGD(format, ...)
+ ALOGD_IF(cond, format, ...)
+ SLOGD_IF(cond, format, ...)
+ RLOGD_IF(cond, format, ...)
+ IF_ALOGD()
+ ALOGI(format, ...)
+ SLOGI(format, ...)
+ RLOGI(format, ...)
+ ALOGI_IF(cond, format, ...)
+ SLOGI_IF(cond, format, ...)
+ RLOGI_IF(cond, format, ...)
+ IF_ALOGI()
+ ALOGW(format, ...)
+ SLOGW(format, ...)
+ RLOGW(format, ...)
+ ALOGW_IF(cond, format, ...)
+ SLOGW_IF(cond, format, ...)
+ RLOGW_IF(cond, format, ...)
+ IF_ALOGW()
+ ALOGE(format, ...)
+ SLOGE(format, ...)
+ RLOGE(format, ...)
+ ALOGE_IF(cond, format, ...)
+ SLOGE_IF(cond, format, ...)
+ RLOGE_IF(cond, format, ...)
+ IF_ALOGE()
+ LOG_FATAL(format, ...)
+ LOG_ALWAYS_FATAL(format, ...)
+ LOG_FATAL_IF(cond, format, ...)
+ LOG_ALWAYS_FATAL_IF(cond, format, ...)
+ ALOG_ASSERT(cond, format, ...)
+ LOG_EVENT_INT(tag, value)
+ LOG_EVENT_LONG(tag, value)
+
+ clockid_t android_log_clockid()
+
+ log_id_t android_logger_get_id(struct logger *logger)
+ int android_logger_clear(struct logger *logger)
+ int android_logger_get_log_size(struct logger *logger)
+ int android_logger_get_log_readable_size(struct logger *logger)
+ int android_logger_get_log_version(struct logger *logger)
+
+ struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid)
+ struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id)
+ struct logger_list *android_logger_list_open(log_id_t id, int mode, unsigned int tail, pid_t pid)
+ int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg)
+ void android_logger_list_free(struct logger_list *logger_list)
+
+ log_id_t android_name_to_log_id(const char *logName)
+ const char *android_log_id_to_name(log_id_t log_id)
+
+ android_log_context create_android_logger(uint32_t tag)
+
+ int android_log_write_list_begin(android_log_context ctx)
+ int android_log_write_list_end(android_log_context ctx)
+
+ int android_log_write_int32(android_log_context ctx, int32_t value)
+ int android_log_write_int64(android_log_context ctx, int64_t value)
+ int android_log_write_string8(android_log_context ctx, const char *value)
+ int android_log_write_string8_len(android_log_context ctx, const char *value, size_t maxlen)
+ int android_log_write_float32(android_log_context ctx, float value)
+
+ int android_log_write_list(android_log_context ctx, log_id_t id = LOG_ID_EVENTS)
+
+ android_log_context create_android_log_parser(const char *msg, size_t len)
+ android_log_list_element android_log_read_next(android_log_context ctx)
+ android_log_list_element android_log_peek_next(android_log_context ctx)
+
+ int android_log_destroy(android_log_context *ctx)
+
+Description
+-----------
+
+liblog represents an interface to the volatile Android Logging system for NDK (Native) applications
+and libraries. Interfaces for either writing or reading logs. The log buffers are divided up in
+Main, System, Radio and Events sub-logs.
+
+The logging interfaces are a series of macros, all of which can be overridden individually in order
+to control the verbosity of the application or library. `[ASR]LOG[VDIWE]` calls are used to log to
+BAsic, System or Radio sub-logs in either the Verbose, Debug, Info, Warning or Error priorities.
+`[ASR]LOG[VDIWE]_IF` calls are used to perform thus based on a condition being true.
+`IF_ALOG[VDIWE]` calls are true if the current `LOG_TAG` is enabled at the specified priority.
+`LOG_ALWAYS_FATAL` is used to `ALOG` a message, then kill the process. `LOG_FATAL` call is a
+variant of `LOG_ALWAYS_FATAL`, only enabled in engineering, and not release builds. `ALOG_ASSERT`
+is used to `ALOG` a message if the condition is false; the condition is part of the logged message.
+`LOG_EVENT_(INT|LONG)` is used to drop binary content into the Events sub-log.
+
+The log reading interfaces permit opening the logs either singly or multiply, retrieving a log entry
+at a time in time sorted order, optionally limited to a specific pid and tail of the log(s) and
+finally a call closing the logs. A single log can be opened with `android_logger_list_open()`; or
+multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
+`android_logger_open()` for each log id. Each entry can be retrieved with
+`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`. The logs
+should be opened with an `ANDROID_LOG_RDONLY` mode. `ANDROID_LOG_NONBLOCK` mode will report when
+the log reading is done with an `EAGAIN` error return code, otherwise the
+`android_logger_list_read()` call will block for new entries.
+
+The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
+the reader until the buffer is about to prune at the start time then proceed to dumping content.
+
+The `ANDROID_LOG_PSTORE` mode flag to the `android_logger_open()` is used to switch from the active
+logs to the persistent logs from before the last reboot.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_clear()` function to empty the sub-log. It is recommended to only open log
+`ANDROID_LOG_WRONLY` in that case.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_get_log_(size|readable_size|version)` to retrieve the sub-log maximum size, readable
+size and log buffer format protocol version respectively. `android_logger_get_id()` returns the id
+that was used when opening the sub-log. It is recommended to open the log `ANDROID_LOG_RDONLY` in
+these cases.
+
+Errors
+------
+
+If messages fail, a negative error code will be returned to the caller.
+
+The `-ENOTCONN` return code indicates that the logger daemon is stopped.
+
+The `-EBADF` return code indicates that the log access point can not be opened, or the log buffer id
+is out of range.
+
+For the `-EAGAIN` return code, this means that the logging message was temporarily backed-up either
+because of Denial Of Service (DOS) logging pressure from some chatty application or service in the
+Android system, or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen. To aid in
+diagnosing the occurence of this, a binary event from liblog will be sent to the log daemon once a
+new message can get through indicating how many messages were dropped as a result. Please take
+action to resolve the structural problems at the source.
+
+It is generally not advised for the caller to retry the `-EAGAIN` return code as this will only make
+the problem(s) worse and cause your application to temporarily drop to the logger daemon priority,
+BATCH scheduling policy and background task cgroup. If you require a group of messages to be passed
+atomically, merge them into one message with embedded newlines to the maximum length
+`LOGGER_ENTRY_MAX_PAYLOAD`.
+
+Other return codes from writing operation can be returned. Since the library retries on `EINTR`,
+`-EINTR` should never be returned.
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
new file mode 100644
index 000000000..fef29c921
--- /dev/null
+++ b/liblog/README.protocol.md
@@ -0,0 +1,49 @@
+# liblog -> logd
+
+The data that liblog sends to logd is represented below.
+
+ struct {
+ android_log_header_t header;
+ union {
+ struct {
+ char prio;
+ char tag[...];
+ char message[...];
+ } string;
+ struct {
+ android_event_header_t event_header;
+ android_event_*_t payload[...];
+ } binary;
+ };
+ };
+
+The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
+
+## header
+
+The header is added immediately before sending the log message to logd.
+
+## `string` payload
+
+The `string` part of the union is for normal buffers (main, system, radio, etc) and consists of a
+single character priority, followed by a variable length null terminated string for the tag, and
+finally a variable length null terminated string for the message.
+
+This payload is used for the `__android_log_buf_write()` family of functions.
+
+## `binary` payload
+
+The `binary` part of the union is for binary buffers (events, security, etc) and consists of an
+android_event_header_t struct followed by a variable number of android_event_*_t
+(android_event_list_t, android_event_int_t, etc) structs.
+
+If multiple android_event_*_t elements are present, then they must be in a list and the first
+element in payload must be an android_event_list_t.
+
+This payload is used for the `__android_log_bwrite()` family of functions. It is additionally used
+for `android_log_write_list()` and the related functions that manipulate event lists.
+
+# logd -> liblog
+
+logd sends a `logger_entry` struct to liblog followed by the payload. The payload is identical to
+the payloads defined above. The max size of the entire message from logd is LOGGER_ENTRY_MAX_LEN.
diff --git a/liblog/event.logtags b/liblog/event.logtags
new file mode 100644
index 000000000..0a3b6501b
--- /dev/null
+++ b/liblog/event.logtags
@@ -0,0 +1,37 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# s: Number of seconds (monotonic time)
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+1006 liblog (dropped|1)
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
new file mode 100644
index 000000000..51c5e60a8
--- /dev/null
+++ b/liblog/event_tag_map.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <functional>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include <log/event_tag_map.h>
+#include <log/log_properties.h>
+#include <private/android_logger.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+
+#include "logd_reader.h"
+
+#define OUT_TAG "EventTagMap"
+
+class MapString {
+ private:
+ const std::string* alloc; // HAS-AN
+ const std::string_view str; // HAS-A
+
+ public:
+ operator const std::string_view() const {
+ return str;
+ }
+
+ const char* data() const {
+ return str.data();
+ }
+ size_t length() const {
+ return str.length();
+ }
+
+ bool operator==(const MapString& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!=(const MapString& rval) const {
+ return !(*this == rval);
+ }
+
+ MapString(const char* str, size_t len) : alloc(NULL), str(str, len) {
+ }
+ explicit MapString(const std::string& str)
+ : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
+ }
+ MapString(MapString&& rval) noexcept
+ : alloc(rval.alloc), str(rval.data(), rval.length()) {
+ rval.alloc = NULL;
+ }
+ explicit MapString(const MapString& rval)
+ : alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
+ str(alloc ? alloc->data() : rval.data(), rval.length()) {
+ }
+
+ ~MapString() {
+ if (alloc) delete alloc;
+ }
+};
+
+// Hash for MapString
+template <>
+struct std::hash<MapString>
+ : public std::unary_function<const MapString&, size_t> {
+ size_t operator()(const MapString& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::string_view>()(std::string_view(__t));
+ }
+};
+
+typedef std::pair<MapString, MapString> TagFmt;
+
+template <>
+struct std::hash<TagFmt> : public std::unary_function<const TagFmt&, size_t> {
+ size_t operator()(const TagFmt& __t) const noexcept {
+ // Tag is typically unique. Will cost us an extra 100ns for the
+ // unordered_map lookup if we instead did a hash that combined
+ // both of tag and fmt members, e.g.:
+ //
+ // return std::hash<MapString>()(__t.first) ^
+ // std::hash<MapString>()(__t.second);
+ return std::hash<MapString>()(__t.first);
+ }
+};
+
+// Map
+struct EventTagMap {
+#define NUM_MAPS 2
+ // memory-mapped source file; we get strings from here
+ void* mapAddr[NUM_MAPS];
+ size_t mapLen[NUM_MAPS];
+
+ private:
+ std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+ std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
+ std::unordered_map<MapString, uint32_t> Tag2Idx;
+ // protect unordered sets
+ android::RWLock rwlock;
+
+ public:
+ EventTagMap() {
+ memset(mapAddr, 0, sizeof(mapAddr));
+ memset(mapLen, 0, sizeof(mapLen));
+ }
+
+ ~EventTagMap() {
+ Idx2TagFmt.clear();
+ TagFmt2Idx.clear();
+ Tag2Idx.clear();
+ for (size_t which = 0; which < NUM_MAPS; ++which) {
+ if (mapAddr[which]) {
+ munmap(mapAddr[which], mapLen[which]);
+ mapAddr[which] = 0;
+ }
+ }
+ }
+
+ bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+ const TagFmt* find(uint32_t tag) const;
+ int find(TagFmt&& tagfmt) const;
+ int find(MapString&& tag) const;
+};
+
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
+ bool verbose) {
+ bool ret = true;
+ static const char errorFormat[] =
+ OUT_TAG ": duplicate tag entries %" PRIu32 ":%.*s:%.*s and %" PRIu32
+ ":%.*s:%.*s)\n";
+ android::RWLock::AutoWLock writeLock(rwlock);
+ {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ it = Idx2TagFmt.find(tag);
+ if (it != Idx2TagFmt.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
+ it->second.first.data(), (int)it->second.second.length(),
+ it->second.second.data(), tag, (int)tagfmt.first.length(),
+ tagfmt.first.data(), (int)tagfmt.second.length(),
+ tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
+ }
+ }
+
+ {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ it = TagFmt2Idx.find(tagfmt);
+ if (it != TagFmt2Idx.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat, it->second, (int)it->first.first.length(),
+ it->first.first.data(), (int)it->first.second.length(),
+ it->first.second.data(), tag, (int)tagfmt.first.length(),
+ tagfmt.first.data(), (int)tagfmt.second.length(),
+ tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
+ }
+ }
+
+ {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ it = Tag2Idx.find(tagfmt.first);
+ if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
+ Tag2Idx.erase(it);
+ it = Tag2Idx.end();
+ }
+ if (it == Tag2Idx.end()) {
+ Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
+ }
+ }
+
+ return ret;
+}
+
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = Idx2TagFmt.find(tag);
+ if (it == Idx2TagFmt.end()) return NULL;
+ return &(it->second);
+}
+
+int EventTagMap::find(TagFmt&& tagfmt) const {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = TagFmt2Idx.find(std::move(tagfmt));
+ if (it == TagFmt2Idx.end()) return -1;
+ return it->second;
+}
+
+int EventTagMap::find(MapString&& tag) const {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = Tag2Idx.find(std::move(tag));
+ if (it == Tag2Idx.end()) return -1;
+ return it->second;
+}
+
+// The position after the end of a valid section of the tag string,
+// caller makes sure delimited appropriately.
+static const char* endOfTag(const char* cp) {
+ while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
+ return cp;
+}
+
+// Scan one tag line.
+//
+// "pData" should be pointing to the first digit in the tag number. On
+// successful return, it will be pointing to the last character in the
+// tag line (i.e. the character before the start of the next line).
+//
+// lineNum = 0 removes verbose comments and requires us to cache the
+// content rather than make direct raw references since the content
+// will disappear after the call. A non-zero lineNum means we own the
+// data and it will outlive the call.
+//
+// Returns 0 on success, nonzero on failure.
+static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+ char* ep;
+ unsigned long val = strtoul(pData, &ep, 10);
+ const char* cp = ep;
+ if (cp == pData) {
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
+ }
+ errno = EINVAL;
+ return -1;
+ }
+
+ uint32_t tagIndex = val;
+ if (tagIndex != val) {
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
+ }
+ errno = ERANGE;
+ return -1;
+ }
+
+ while ((*++cp != '\n') && isspace(*cp)) {
+ }
+
+ if (*cp == '\n') {
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
+ }
+ errno = EINVAL;
+ return -1;
+ }
+
+ const char* tag = cp;
+ cp = endOfTag(cp);
+ size_t tagLen = cp - tag;
+
+ if (!isspace(*cp)) {
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
+ lineNum);
+ }
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (isspace(*cp) && (*cp != '\n')) ++cp;
+ const char* fmt = NULL;
+ size_t fmtLen = 0;
+ if (*cp && (*cp != '#')) {
+ fmt = cp;
+ while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
+ while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+ fmtLen = cp - fmt;
+ }
+
+ // KISS Only report identicals if they are global
+ // Ideally we want to check if there are identicals
+ // recorded for the same uid, but recording that
+ // unused detail in our database is too burdensome.
+ bool verbose = true;
+ while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
+ if (*cp == '#') {
+ do {
+ ++cp;
+ } while (isspace(*cp) && (*cp != '\n'));
+ verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
+ }
+
+ while (*cp && (*cp != '\n')) ++cp;
+#ifdef DEBUG
+ fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
+#endif
+ pData = cp;
+
+ if (lineNum) {
+ if (map->emplaceUnique(tagIndex,
+ TagFmt(std::make_pair(MapString(tag, tagLen),
+ MapString(fmt, fmtLen))),
+ verbose)) {
+ return 0;
+ }
+ } else {
+ // cache
+ if (map->emplaceUnique(
+ tagIndex,
+ TagFmt(std::make_pair(MapString(std::string(tag, tagLen)),
+ MapString(std::string(fmt, fmtLen)))))) {
+ return 0;
+ }
+ }
+ errno = EMLINK;
+ return -1;
+}
+
+static const char* eventTagFiles[NUM_MAPS] = {
+ EVENT_TAG_MAP_FILE, "/dev/event-log-tags",
+};
+
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map, size_t which) {
+ const char* cp = static_cast<char*>(map->mapAddr[which]);
+ size_t len = map->mapLen[which];
+ const char* endp = cp + len;
+
+ // insist on EOL at EOF; simplifies parsing and null-termination
+ if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+ fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
+ which, len);
+#endif
+ if (which) { // do not propagate errors for other files
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+ }
+
+ bool lineStart = true;
+ int lineNum = 1;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = true;
+ lineNum++;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ // comment; just scan to end
+ lineStart = false;
+ } else if (isdigit(*cp)) {
+ // looks like a tag; scan it out
+ if (scanTagLine(map, cp, lineNum) != 0) {
+ if (!which || (errno != EMLINK)) {
+ return -1;
+ }
+ }
+ lineNum++; // we eat the '\n'
+ // leave lineStart==true
+ } else if (isspace(*cp)) {
+ // looks like leading whitespace; keep scanning
+ } else {
+ fprintf(stderr,
+ OUT_TAG
+ ": unexpected chars (0x%02x) in tag number on line %d\n",
+ *cp, lineNum);
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ // this is a blank or comment line
+ }
+ cp++;
+ }
+
+ return 0;
+}
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+EventTagMap* android_openEventTagMap(const char* fileName) {
+ EventTagMap* newTagMap;
+ off_t end[NUM_MAPS];
+ int save_errno, fd[NUM_MAPS];
+ size_t which;
+
+ memset(fd, -1, sizeof(fd));
+ memset(end, 0, sizeof(end));
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
+ if (fd[which] < 0) {
+ if (!which) {
+ save_errno = errno;
+ fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n", tagfile,
+ strerror(save_errno));
+ goto fail_errno;
+ }
+ continue;
+ }
+ end[which] = lseek(fd[which], 0L, SEEK_END);
+ save_errno = errno;
+ (void)lseek(fd[which], 0L, SEEK_SET);
+ if (!which && (end[0] < 0)) {
+ fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n", tagfile,
+ strerror(save_errno));
+ goto fail_close;
+ }
+ if (fileName) break; // Only allow one as specified
+ }
+
+ newTagMap = new EventTagMap;
+ if (newTagMap == NULL) {
+ save_errno = errno;
+ goto fail_close;
+ }
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (fd[which] >= 0) {
+ newTagMap->mapAddr[which] =
+ mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
+ which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
+ save_errno = errno;
+ close(fd[which]); /* fd DONE */
+ fd[which] = -1;
+ if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
+ (newTagMap->mapAddr[which] != NULL)) {
+ newTagMap->mapLen[which] = end[which];
+ } else if (!which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n", tagfile,
+ strerror(save_errno));
+ goto fail_unmap;
+ }
+ }
+ }
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (parseMapLines(newTagMap, which) != 0) {
+ delete newTagMap;
+ return NULL;
+ }
+ /* See 'fd DONE' comments above and below, no need to clean up here */
+ }
+
+ return newTagMap;
+
+fail_unmap:
+ save_errno = EINVAL;
+ delete newTagMap;
+fail_close:
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
+fail_errno:
+ errno = save_errno;
+ return NULL;
+}
+
+// Close the map.
+void android_closeEventTagMap(EventTagMap* map) {
+ if (map) delete map;
+}
+
+// Cache miss, go to logd to acquire a public reference.
+// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
+static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
+ // call event tag service to arrange for a new tag
+ char* buf = NULL;
+ // Can not use android::base::StringPrintf, asprintf + free instead.
+ static const char command_template[] = "getEventTag id=%u";
+ int ret = asprintf(&buf, command_template, tag);
+ if (ret > 0) {
+ // Add some buffer margin for an estimate of the full return content.
+ size_t size =
+ ret - strlen(command_template) +
+ strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+ if (size > (size_t)ret) {
+ char* np = static_cast<char*>(realloc(buf, size));
+ if (np) {
+ buf = np;
+ } else {
+ size = ret;
+ }
+ } else {
+ size = ret;
+ }
+#ifdef __ANDROID__
+ // Ask event log tag service for an existing entry
+ if (SendLogdControlMessage(buf, size) >= 0) {
+ buf[size - 1] = '\0';
+ char* ep;
+ unsigned long val = strtoul(buf, &ep, 10); // return size
+ const char* cp = ep;
+ if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+ ++cp;
+ if (!scanTagLine(map, cp, 0)) {
+ free(buf);
+ return map->find(tag);
+ }
+ }
+ }
+#endif
+ free(buf);
+ }
+ return NULL;
+}
+
+// Look up an entry in the map.
+const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
+ if (len) *len = 0;
+ const TagFmt* str = map->find(tag);
+ if (!str) {
+ str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+ }
+ if (!str) return NULL;
+ if (len) *len = str->first.length();
+ return str->first.data();
+}
+
+// Look up an entry in the map.
+const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
+ if (len) *len = 0;
+ const TagFmt* str = map->find(tag);
+ if (!str) {
+ str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+ }
+ if (!str) return NULL;
+ if (len) *len = str->second.length();
+ return str->second.data();
+}
+
+// This function is deprecated and replaced with android_lookupEventTag_len
+// since it will cause the map to change from Shared and backed by a file,
+// to Private Dirty and backed up by swap, albeit highly compressible. By
+// deprecating this function everywhere, we save 100s of MB of memory space.
+const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag) {
+ size_t len;
+ const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+
+ if (!tagStr) return tagStr;
+ char* cp = const_cast<char*>(tagStr);
+ cp += len;
+ if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
+ return tagStr;
+}
+
+// Look up tagname, generate one if necessary, and return a tag
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
+ const char* ep = endOfTag(tagname);
+ size_t len = ep - tagname;
+ if (!len || *ep) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
+ !__android_log_is_loggable_len(prio, tagname, len,
+ __android_log_is_debuggable()
+ ? ANDROID_LOG_VERBOSE
+ : ANDROID_LOG_DEBUG)) {
+ errno = EPERM;
+ return -1;
+ }
+
+ if (!format) format = "";
+ ssize_t fmtLen = strlen(format);
+ int ret = map->find(TagFmt(
+ std::make_pair(MapString(tagname, len), MapString(format, fmtLen))));
+ if (ret != -1) return ret;
+
+ // call event tag service to arrange for a new tag
+ char* buf = NULL;
+ // Can not use android::base::StringPrintf, asprintf + free instead.
+ static const char command_template[] = "getEventTag name=%s format=\"%s\"";
+ ret = asprintf(&buf, command_template, tagname, format);
+ if (ret > 0) {
+ // Add some buffer margin for an estimate of the full return content.
+ char* cp;
+ size_t size =
+ ret - strlen(command_template) +
+ strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+ if (size > (size_t)ret) {
+ cp = static_cast<char*>(realloc(buf, size));
+ if (cp) {
+ buf = cp;
+ } else {
+ size = ret;
+ }
+ } else {
+ size = ret;
+ }
+#ifdef __ANDROID__
+ // Ask event log tag service for an allocation
+ if (SendLogdControlMessage(buf, size) >= 0) {
+ buf[size - 1] = '\0';
+ unsigned long val = strtoul(buf, &cp, 10); // return size
+ if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+ val = strtoul(cp + 1, &cp, 10); // allocated tag number
+ if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
+ free(buf);
+ ret = val;
+ // cache
+ map->emplaceUnique(ret, TagFmt(std::make_pair(
+ MapString(std::string(tagname, len)),
+ MapString(std::string(format, fmtLen)))));
+ return ret;
+ }
+ }
+ }
+#endif
+ free(buf);
+ }
+
+ // Hail Mary
+ ret = map->find(MapString(tagname, len));
+ if (ret == -1) errno = ESRCH;
+ return ret;
+}
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
new file mode 100644
index 000000000..512c7cd7b
--- /dev/null
+++ b/liblog/include/android/log.h
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * @addtogroup Logging
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * Support routines to send messages to the Android log buffer,
+ * which can later be accessed through the `logcat` utility.
+ *
+ * Each log message must have
+ * - a priority
+ * - a log tag
+ * - some text
+ *
+ * The tag normally corresponds to the component that emits the log message,
+ * and should be reasonably small.
+ *
+ * Log message text may be truncated to less than an implementation-specific
+ * limit (1023 bytes).
+ *
+ * Note that a newline character ("\n") will be appended automatically to your
+ * log message, if not already there. It is not possible to send several
+ * messages and have them appear on a single line in logcat.
+ *
+ * Please use logging in moderation:
+ *
+ * - Sending log messages eats CPU and slow down your application and the
+ * system.
+ *
+ * - The circular log buffer is pretty small, so sending many messages
+ * will hide other important log messages.
+ *
+ * - In release builds, only send log messages to account for exceptional
+ * conditions.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(x)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Android log priority values, in increasing order of priority.
+ */
+typedef enum android_LogPriority {
+ /** For internal use only. */
+ ANDROID_LOG_UNKNOWN = 0,
+ /** The default priority, for internal use only. */
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ /** Verbose logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_VERBOSE,
+ /** Debug logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_DEBUG,
+ /** Informational logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_INFO,
+ /** Warning logging. For use with recoverable failures. */
+ ANDROID_LOG_WARN,
+ /** Error logging. For use with unrecoverable failures. */
+ ANDROID_LOG_ERROR,
+ /** Fatal logging. For use when aborting. */
+ ANDROID_LOG_FATAL,
+ /** For internal use only. */
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+/**
+ * Writes the constant string `text` to the log, with priority `prio` and tag
+ * `tag`.
+ */
+int __android_log_write(int prio, const char* tag, const char* text);
+
+/**
+ * Writes a formatted string to the log, with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ */
+int __android_log_print(int prio, const char* tag, const char* fmt, ...)
+ __attribute__((__format__(printf, 3, 4)));
+
+/**
+ * Equivalent to `__android_log_print`, but taking a `va_list`.
+ * (If `__android_log_print` is like `printf`, this is like `vprintf`.)
+ */
+int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
+ __attribute__((__format__(printf, 3, 0)));
+
+/**
+ * Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
+ * stderr, before calling
+ * [abort(3)](http://man7.org/linux/man-pages/man3/abort.3.html).
+ *
+ * If `fmt` is non-null, `cond` is unused. If `fmt` is null, the string
+ * `Assertion failed: %s` is used with `cond` as the string argument.
+ * If both `fmt` and `cond` are null, a default string is provided.
+ *
+ * Most callers should use
+ * [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
+ * `&lt;assert.h&gt;` instead, or the `__assert` and `__assert2` functions
+ * provided by bionic if more control is needed. They support automatically
+ * including the source filename and line number more conveniently than this
+ * function.
+ */
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
+ __attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
+
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
+typedef enum log_id {
+ LOG_ID_MIN = 0,
+
+ /** The main log buffer. This is the only log buffer available to apps. */
+ LOG_ID_MAIN = 0,
+ /** The radio log buffer. */
+ LOG_ID_RADIO = 1,
+ /** The event log buffer. */
+ LOG_ID_EVENTS = 2,
+ /** The system log buffer. */
+ LOG_ID_SYSTEM = 3,
+ /** The crash log buffer. */
+ LOG_ID_CRASH = 4,
+ /** The statistics log buffer. */
+ LOG_ID_STATS = 5,
+ /** The security log buffer. */
+ LOG_ID_SECURITY = 6,
+ /** The kernel log buffer. */
+ LOG_ID_KERNEL = 7,
+
+ LOG_ID_MAX,
+
+ /** Let the logging function choose the best log target. */
+ LOG_ID_DEFAULT = 0x7FFFFFFF
+} log_id_t;
+
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ *
+ * Apps should use __android_log_print() instead.
+ */
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+ __attribute__((__format__(printf, 4, 5)));
+
+/**
+ * Logger data struct used for writing log messages to liblog via __android_log_write_logger_data()
+ * and sending log messages to user defined loggers specified in __android_log_set_logger().
+ */
+struct __android_log_message {
+ size_t
+ struct_size; /** Must be set to sizeof(__android_log_message) and is used for versioning. */
+ int32_t buffer_id; /** {@link log_id_t} values. */
+ int32_t priority; /** {@link android_LogPriority} values. */
+ const char* tag; /** The tag for the log message. */
+ const char* file; /** Optional file name, may be set to nullptr. */
+ uint32_t line; /** Optional line number, ignore if file is nullptr. */
+ const char* message; /** The log message itself. */
+};
+
+/**
+ * Prototype for the 'logger' function that is called for every log message.
+ */
+typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
+/**
+ * Prototype for the 'abort' function that is called when liblog will abort due to
+ * __android_log_assert() failures.
+ */
+typedef void (*__android_aborter_function)(const char* abort_message);
+
+#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
+/**
+ * Writes the log message specified by log_message. log_message includes additional file name and
+ * line number information that a logger may use. log_message is versioned for backwards
+ * compatibility.
+ * This assumes that loggability has already been checked through __android_log_is_loggable().
+ * Higher level logging libraries, such as libbase, first check loggability, then format their
+ * buffers, then pass the message to liblog via this function, and therefore we do not want to
+ * duplicate the loggability check here.
+ *
+ * @param log_message the log message itself, see {@link __android_log_message}.
+ *
+ * Available since API level 30.
+ */
+void __android_log_write_log_message(struct __android_log_message* log_message) __INTRODUCED_IN(30);
+
+/**
+ * Sets a user defined logger function. All log messages sent to liblog will be set to the
+ * function pointer specified by logger for processing. It is not expected that log messages are
+ * already terminated with a new line. This function should add new lines if required for line
+ * separation.
+ *
+ * @param logger the new function that will handle log messages.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30);
+
+/**
+ * Writes the log message to logd. This is an __android_logger_function and can be provided to
+ * __android_log_set_logger(). It is the default logger when running liblog on a device.
+ *
+ * @param log_message the log message to write, see {@link __android_log_message}.
+ *
+ * Available since API level 30.
+ */
+void __android_log_logd_logger(const struct __android_log_message* log_message) __INTRODUCED_IN(30);
+
+/**
+ * Writes the log message to stderr. This is an __android_logger_function and can be provided to
+ * __android_log_set_logger(). It is the default logger when running liblog on host.
+ *
+ * @param log_message the log message to write, see {@link __android_log_message}.
+ *
+ * Available since API level 30.
+ */
+void __android_log_stderr_logger(const struct __android_log_message* log_message)
+ __INTRODUCED_IN(30);
+
+/**
+ * Sets a user defined aborter function that is called for __android_log_assert() failures. This
+ * user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
+ * required to.
+ *
+ * @param aborter the new aborter function, see {@link __android_aborter_function}.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30);
+
+/**
+ * Calls the stored aborter function. This allows for other logging libraries to use the same
+ * aborter function by calling this function in liblog.
+ *
+ * @param abort_message an additional message supplied when aborting, for example this is used to
+ * call android_set_abort_message() in __android_log_default_aborter().
+ *
+ * Available since API level 30.
+ */
+void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30);
+
+/**
+ * Sets android_set_abort_message() on device then aborts(). This is the default aborter.
+ *
+ * @param abort_message an additional message supplied when aborting. This functions calls
+ * android_set_abort_message() with its contents.
+ *
+ * Available since API level 30.
+ */
+void __android_log_default_aborter(const char* abort_message) __attribute__((noreturn))
+__INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed. A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log. If only one is set, then that value is used to determine the
+ * minimum priority needed. If none are set, then default_priority is used.
+ *
+ * @param prio the priority to test, takes {@link android_LogPriority} values.
+ * @param tag the tag to test.
+ * @param len the length of the tag.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
+ __INTRODUCED_IN(30);
+
+/**
+ * Sets the minimum priority that will be logged for this process.
+ *
+ * @param priority the new minimum priority to set, takes @{link android_LogPriority} values.
+ * @return the previous set minimum priority as @{link android_LogPriority} values, or
+ * ANDROID_LOG_DEFAULT if none was set.
+ *
+ * Available since API level 30.
+ */
+int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30);
+
+/**
+ * Gets the minimum priority that will be logged for this process. If none has been set by a
+ * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
+ *
+ * @return the current minimum priority as @{link android_LogPriority} values, or
+ * ANDROID_LOG_DEFAULT if none is set.
+ *
+ * Available since API level 30.
+ */
+int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30);
+
+/**
+ * Sets the default tag if no tag is provided when writing a log message. Defaults to
+ * getprogname(). This truncates tag to the maximum log message size, though appropriate tags
+ * should be much smaller.
+ *
+ * @param tag the new log tag.
+ *
+ * Available since API level 30.
+ */
+void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
new file mode 100644
index 000000000..f7ec208b3
--- /dev/null
+++ b/liblog/include/log/event_tag_map.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index. Returns the tag string, or NULL if not found.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag)
+ __attribute__((
+ deprecated("use android_lookupEventTag_len() instead to minimize "
+ "MAP_PRIVATE copy-on-write memory impact")));
+
+/*
+ * Look up a tag by index. Returns the tag string & string length, or NULL if
+ * not found. Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len,
+ unsigned int tag);
+
+/*
+ * Look up a format by index. Returns the format string & string length,
+ * or NULL if not found. Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
+ unsigned int tag);
+
+/*
+ * Look up tagname, generate one if necessary, and return a tag
+ */
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
+ const char* format, int prio);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
new file mode 100644
index 000000000..c116add4c
--- /dev/null
+++ b/liblog/include/log/log.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2005-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/* Too many in the ecosystem assume these are included */
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+#include <stdint.h> /* uint16_t, int32_t */
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_safetynet.h>
+#include <log/log_system.h>
+#include <log/log_time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * Event logging.
+ */
+
+/*
+ * The following should not be used directly.
+ */
+
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void* payload,
+ size_t len);
+int __android_log_bswrite(int32_t tag, const char* payload);
+
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
+
+#define android_bWriteLog(tag, payload, len) \
+ __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+ __android_log_btwrite(tag, type, payload, len)
+
+/*
+ * Event log entry types.
+ */
+#ifndef __AndroidEventLogType_defined
+#define __AndroidEventLogType_defined
+typedef enum {
+ /* Special markers for android_log_list_element type */
+ EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
+ EVENT_TYPE_UNKNOWN = '?', /* protocol error */
+
+ /* must match with declaration in java/android/android/util/EventLog.java */
+ EVENT_TYPE_INT = 0, /* int32_t */
+ EVENT_TYPE_LONG = 1, /* int64_t */
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+ EVENT_TYPE_FLOAT = 4,
+} AndroidEventLogType;
+#endif
+#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
+#define typeof_AndroidEventLogType unsigned char
+
+#ifndef LOG_EVENT_INT
+#define LOG_EVENT_INT(_tag, _value) \
+ { \
+ int intBuf = _value; \
+ (void)android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_LONG
+#define LOG_EVENT_LONG(_tag, _value) \
+ { \
+ long long longBuf = _value; \
+ (void)android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_FLOAT
+#define LOG_EVENT_FLOAT(_tag, _value) \
+ { \
+ float floatBuf = _value; \
+ (void)android_btWriteLog(_tag, EVENT_TYPE_FLOAT, &floatBuf, \
+ sizeof(floatBuf)); \
+ }
+#endif
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(_tag, _value) \
+ (void)__android_log_bswrite(_tag, _value);
+#endif
+
+#ifdef __linux__
+
+clockid_t android_log_clockid(void);
+
+#endif /* __linux__ */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Release any logger resources (a new log write will immediately re-acquire)
+ *
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
+ */
+void __android_log_close(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
new file mode 100644
index 000000000..deadf2060
--- /dev/null
+++ b/liblog/include/log/log_event_list.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2005-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+#include <string>
+#endif
+
+#include <log/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For manipulating lists of events. */
+
+#define ANDROID_MAX_LIST_NEST_DEPTH 8
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+typedef struct android_log_context_internal* android_log_context;
+
+/*
+ * Elements returned when reading a list of events.
+ */
+typedef struct {
+ AndroidEventLogType type;
+ uint16_t complete;
+ uint16_t len;
+ union {
+ int32_t int32;
+ int64_t int64;
+ char* string;
+ float float32;
+ } data;
+} android_log_list_element;
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ * elements, we will manufacturer a list to embrace it for your API
+ * convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+ size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/*
+ * Creates a context from a raw buffer representing a list of events to be read.
+ */
+android_log_context create_android_log_parser(const char* msg, size_t len);
+
+android_log_list_element android_log_read_next(android_log_context ctx);
+android_log_list_element android_log_peek_next(android_log_context ctx);
+
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+ const char* msg, size_t len);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+/* android_log_list C++ helpers */
+extern "C++" {
+class android_log_event_list {
+ private:
+ android_log_context ctx;
+ int ret;
+
+ android_log_event_list(const android_log_event_list&) = delete;
+ void operator=(const android_log_event_list&) = delete;
+
+ public:
+ explicit android_log_event_list(int tag) : ret(0) {
+ ctx = create_android_logger(static_cast<uint32_t>(tag));
+ }
+ ~android_log_event_list() {
+ android_log_destroy(&ctx);
+ }
+
+ int close() {
+ int retval = android_log_destroy(&ctx);
+ if (retval < 0) ret = retval;
+ return retval;
+ }
+
+ /* To allow above C calls to use this class as parameter */
+ operator android_log_context() const {
+ return ctx;
+ }
+
+ /* return errors or transmit status */
+ int status() const {
+ return ret;
+ }
+
+ int begin() {
+ int retval = android_log_write_list_begin(ctx);
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+ int end() {
+ int retval = android_log_write_list_end(ctx);
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+
+ android_log_event_list& operator<<(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(uint32_t value) {
+ int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(uint64_t value) {
+ int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(const std::string& value) {
+ int retval =
+ android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ android_log_event_list& operator<<(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) ret = retval;
+ return *this;
+ }
+
+ int write(log_id_t id = LOG_ID_EVENTS) {
+ /* facilitate -EBUSY retry */
+ if ((ret == -EBUSY) || (ret > 0)) ret = 0;
+ int retval = android_log_write_list(ctx, id);
+ /* existing errors trump transmission errors */
+ if (!ret) ret = retval;
+ return ret;
+ }
+
+ int operator<<(log_id_t id) {
+ write(id);
+ android_log_destroy(&ctx);
+ return ret;
+ }
+
+ /*
+ * Append<Type> methods removes any integer promotion
+ * confusion, and adds access to string with length.
+ * Append methods are also added for all types for
+ * convenience.
+ */
+
+ bool AppendInt(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendLong(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ bool AppendString(const std::string& value) {
+ int retval =
+ android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+
+ bool Append(const std::string& value) {
+ int retval =
+ android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) ret = retval;
+ return ret;
+ }
+
+ bool AppendFloat(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+
+ template <typename Tvalue>
+ bool Append(Tvalue value) {
+ *this << value;
+ return ret >= 0;
+ }
+
+ bool Append(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) ret = retval;
+ return ret >= 0;
+ }
+};
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
new file mode 100644
index 000000000..c8fafe760
--- /dev/null
+++ b/liblog/include/log/log_id.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag,
+ const char* text);
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+ __attribute__((__format__(printf, 4, 5)));
+
+/*
+ * log_id_t helpers
+ */
+log_id_t android_name_to_log_id(const char* logName);
+const char* android_log_id_to_name(log_id_t log_id);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
new file mode 100644
index 000000000..1bd1c8aec
--- /dev/null
+++ b/liblog/include/log/log_main.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <android/log.h>
+
+__BEGIN_DECLS
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings. See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
+ */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/*
+ * Use __VA_ARGS__ if running a static analyzer,
+ * to avoid warnings of unused variables in __VA_ARGS__.
+ * Use constexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
+ */
+#ifdef __clang_analyzer__
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+ return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif /* __clang_analyzer__ */
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+#define android_writeLog(prio, tag, text) __android_log_write(prio, tag, text)
+
+#define android_printLog(prio, tag, ...) \
+ __android_log_print(prio, tag, __VA_ARGS__)
+
+#define android_vprintLog(prio, cond, tag, ...) \
+ __android_log_vprint(prio, tag, __VA_ARGS__)
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+ android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+ * (happens only in debug builds).
+ */
+
+/* Returns 2nd arg. Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...) second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...) , ##__VA_ARGS__
+
+#define android_printAssert(cond, tag, ...) \
+ __android_log_assert(cond, tag, \
+ __android_second(0, ##__VA_ARGS__, NULL) \
+ __android_rest(__VA_ARGS__))
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#ifndef LOG_ALWAYS_FATAL_IF
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \
+ ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \
+ : ((void)0))
+#endif
+
+#ifndef LOG_ALWAYS_FATAL
+#define LOG_ALWAYS_FATAL(...) \
+ (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
+#endif
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+
+#if LOG_NDEBUG
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+#endif
+
+#else
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+#endif
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current LOG_TAG.
+ */
+#ifndef ALOG_ASSERT
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * C/C++ logging functions. See the logging documentation for API details.
+ *
+ * We'd like these to be available from C code (in case we import some from
+ * somewhere), so this has a C interface.
+ *
+ * The output will be correct when the log file is shared between multiple
+ * threads and/or multiple processes so long as the operating system
+ * supports O_APPEND. These calls have mutex-protected data structures
+ * and so are NOT reentrant. Do not use LOG in a signal handler.
+ */
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef ALOGV
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define ALOGV(...) \
+ do { \
+ __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+ if (false) { \
+ __ALOGV(__VA_ARGS__); \
+ } \
+ } while (false)
+#else
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef ALOGV_IF
+#if LOG_NDEBUG
+#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
+#else
+#define ALOGV_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+ : ((void)0))
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGD_IF
+#define ALOGD_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+ : ((void)0))
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI_IF
+#define ALOGI_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+ : ((void)0))
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW_IF
+#define ALOGW_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : ((void)0))
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE_IF
+#define ALOGE_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+ : ((void)0))
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_ALOGV
+#if LOG_NDEBUG
+#define IF_ALOGV() if (false)
+#else
+#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_ALOGD
+#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_ALOGI
+#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_ALOGW
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_ALOGE
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * ALOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef ALOG
+#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_ALOG
+#define IF_ALOG(priority, tag) if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * IF_ALOG uses android_testLog, but IF_ALOG can be overridden.
+ * android_testLog will remain constant in its purpose as a wrapper
+ * for Android logging filter policy, and can be subject to
+ * change. It can be reused by the developers that override
+ * IF_ALOG as a convenient means to reimplement their policy
+ * over Android.
+ */
+
+/*
+ * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
+ * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
+ * ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if
+ * any other value.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
+
+#if LOG_NDEBUG /* Production */
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
+ ANDROID_LOG_DEBUG) != 0)
+#else
+#define android_testLog(prio, tag) \
+ (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
+ ANDROID_LOG_VERBOSE) != 0)
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+__END_DECLS
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
new file mode 100644
index 000000000..3a8af6deb
--- /dev/null
+++ b/liblog/include/log/log_properties.h
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __android_log_is_debuggable();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
new file mode 100644
index 000000000..8b8a3625f
--- /dev/null
+++ b/liblog/include/log/log_radio.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+#include <log/log_id.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * Simplified macro to send a verbose radio log message using current LOG_TAG.
+ */
+#ifndef RLOGV
+#define __RLOGV(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, \
+ __VA_ARGS__))
+#if LOG_NDEBUG
+#define RLOGV(...) \
+ do { \
+ if (0) { \
+ __RLOGV(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define RLOGV(...) __RLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef RLOGV_IF
+#if LOG_NDEBUG
+#define RLOGV_IF(cond, ...) ((void)0)
+#else
+#define RLOGV_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug radio log message using current LOG_TAG.
+ */
+#ifndef RLOGD
+#define RLOGD(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef RLOGD_IF
+#define RLOGD_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an info radio log message using current LOG_TAG.
+ */
+#ifndef RLOGI
+#define RLOGI(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef RLOGI_IF
+#define RLOGI_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send a warning radio log message using current LOG_TAG.
+ */
+#ifndef RLOGW
+#define RLOGW(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef RLOGW_IF
+#define RLOGW_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an error radio log message using current LOG_TAG.
+ */
+#ifndef RLOGE
+#define RLOGE(...) \
+ ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef RLOGE_IF
+#define RLOGE_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
new file mode 100644
index 000000000..f9c1d691e
--- /dev/null
+++ b/liblog/include/log/log_read.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+/* deal with possible sys/cdefs.h conflict with fcntl.h */
+#ifdef __unused
+#define __unused_defined __unused
+#undef __unused
+#endif
+
+#include <fcntl.h> /* Pick up O_* macros */
+
+/* restore definitions from above */
+#ifdef __unused_defined
+#define __unused __attribute__((__unused__))
+#endif
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+#include <log/log_time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Native log reading interface section. See logcat for sample code.
+ *
+ * The preferred API is an exec of logcat. Likely uses of this interface
+ * are if native code suffers from exec or filtration being too costly,
+ * access to raw information, or parsing is an issue.
+ */
+
+struct logger_entry {
+ uint16_t len; /* length of the payload */
+ uint16_t hdr_size; /* sizeof(struct logger_entry) */
+ int32_t pid; /* generating process's pid */
+ uint32_t tid; /* generating process's tid */
+ uint32_t sec; /* seconds since Epoch */
+ uint32_t nsec; /* nanoseconds */
+ uint32_t lid; /* log id of the payload, bottom 4 bits currently */
+ uint32_t uid; /* generating process's uid */
+};
+
+/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
+ * The maximum size of a log entry which can be read.
+ * An attempt to read less than this amount may result
+ * in read() returning EINVAL.
+ */
+#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
+
+struct log_msg {
+ union {
+ unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+ struct logger_entry entry;
+ } __attribute__((aligned(4)));
+#ifdef __cplusplus
+ /* Matching log_time operators */
+ bool operator==(const log_msg& T) const {
+ return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
+ }
+ bool operator!=(const log_msg& T) const {
+ return !(*this == T);
+ }
+ bool operator<(const log_msg& T) const {
+ return (entry.sec < T.entry.sec) ||
+ ((entry.sec == T.entry.sec) && (entry.nsec < T.entry.nsec));
+ }
+ bool operator>=(const log_msg& T) const {
+ return !(*this < T);
+ }
+ bool operator>(const log_msg& T) const {
+ return (entry.sec > T.entry.sec) ||
+ ((entry.sec == T.entry.sec) && (entry.nsec > T.entry.nsec));
+ }
+ bool operator<=(const log_msg& T) const {
+ return !(*this > T);
+ }
+ uint64_t nsec() const {
+ return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
+ }
+
+ /* packet methods */
+ log_id_t id() {
+ return static_cast<log_id_t>(entry.lid);
+ }
+ char* msg() {
+ unsigned short hdr_size = entry.hdr_size;
+ if (hdr_size >= sizeof(struct log_msg) - sizeof(entry)) {
+ return nullptr;
+ }
+ return reinterpret_cast<char*>(buf) + hdr_size;
+ }
+ unsigned int len() { return entry.hdr_size + entry.len; }
+#endif
+};
+
+struct logger;
+
+log_id_t android_logger_get_id(struct logger* logger);
+
+int android_logger_clear(struct logger* logger);
+long android_logger_get_log_size(struct logger* logger);
+int android_logger_set_log_size(struct logger* logger, unsigned long size);
+long android_logger_get_log_readable_size(struct logger* logger);
+int android_logger_get_log_version(struct logger* logger);
+
+struct logger_list;
+
+ssize_t android_logger_get_statistics(struct logger_list* logger_list,
+ char* buf, size_t len);
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
+ char* buf, size_t len);
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
+
+#define ANDROID_LOG_RDONLY O_RDONLY
+#define ANDROID_LOG_WRONLY O_WRONLY
+#define ANDROID_LOG_RDWR O_RDWR
+#define ANDROID_LOG_ACCMODE O_ACCMODE
+#ifndef O_NONBLOCK
+#define ANDROID_LOG_NONBLOCK 0x00000800
+#else
+#define ANDROID_LOG_NONBLOCK O_NONBLOCK
+#endif
+#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+#define ANDROID_LOG_PSTORE 0x80000000
+
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
+ pid_t pid);
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start,
+ pid_t pid);
+void android_logger_list_free(struct logger_list* logger_list);
+/* In the purest sense, the following two are orthogonal interfaces */
+int android_logger_list_read(struct logger_list* logger_list,
+ struct log_msg* log_msg);
+
+/* Multiple log_id_t opens */
+struct logger* android_logger_open(struct logger_list* logger_list, log_id_t id);
+#define android_logger_close android_logger_free
+/* Single log_id_t open */
+struct logger_list* android_logger_list_open(log_id_t id, int mode,
+ unsigned int tail, pid_t pid);
+#define android_logger_list_close android_logger_list_free
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
new file mode 100644
index 000000000..d3e9b1954
--- /dev/null
+++ b/liblog/include/log/log_safetynet.h
@@ -0,0 +1,29 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define android_errorWriteLog(tag, subTag) \
+ __android_log_error_write(tag, subTag, -1, NULL, 0)
+
+#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
+ __android_log_error_write(tag, subTag, uid, data, dataLen)
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid,
+ const char* data, uint32_t dataLen);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
new file mode 100644
index 000000000..eaec741ea
--- /dev/null
+++ b/liblog/include/log/log_system.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+#include <log/log_id.h>
+
+/*
+ * Normally we strip the effects of ALOGV (VERBOSE messages),
+ * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the
+ * release builds be defining NDEBUG. You can modify this (for
+ * example with "#define LOG_NDEBUG 0" at the top of your source
+ * file) to change that behavior.
+ */
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+/*
+ * Simplified macro to send a verbose system log message using current LOG_TAG.
+ */
+#ifndef SLOGV
+#define __SLOGV(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, \
+ __VA_ARGS__))
+#if LOG_NDEBUG
+#define SLOGV(...) \
+ do { \
+ if (0) { \
+ __SLOGV(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define SLOGV(...) __SLOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef SLOGV_IF
+#if LOG_NDEBUG
+#define SLOGV_IF(cond, ...) ((void)0)
+#else
+#define SLOGV_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug system log message using current LOG_TAG.
+ */
+#ifndef SLOGD
+#define SLOGD(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef SLOGD_IF
+#define SLOGD_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an info system log message using current LOG_TAG.
+ */
+#ifndef SLOGI
+#define SLOGI(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef SLOGI_IF
+#define SLOGI_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an error system log message using current LOG_TAG.
+ */
+#ifndef SLOGE
+#define SLOGE(...) \
+ ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, \
+ __VA_ARGS__))
+#endif
+
+#ifndef SLOGE_IF
+#define SLOGE_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, \
+ LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
new file mode 100644
index 000000000..6b4458cae
--- /dev/null
+++ b/liblog/include/log/log_time.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <time.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+#ifdef __cplusplus
+
+extern "C" {
+
+struct log_time {
+ public:
+ uint32_t tv_sec = 0; /* good to Feb 5 2106 */
+ uint32_t tv_nsec = 0;
+
+ static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
+ static const uint32_t tv_nsec_max = 999999999UL;
+ static const timespec EPOCH;
+
+ log_time() {}
+ explicit log_time(const timespec& T)
+ : tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
+ explicit log_time(uint32_t sec, uint32_t nsec = 0)
+ : tv_sec(sec), tv_nsec(nsec) {
+ }
+#ifdef __linux__
+ explicit log_time(clockid_t id) {
+ timespec T;
+ clock_gettime(id, &T);
+ tv_sec = static_cast<uint32_t>(T.tv_sec);
+ tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+ }
+#endif
+ explicit log_time(const char* T) {
+ const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
+ tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
+ (static_cast<uint32_t>(c[2]) << 16) |
+ (static_cast<uint32_t>(c[3]) << 24);
+ tv_nsec = c[4] | (static_cast<uint32_t>(c[5]) << 8) |
+ (static_cast<uint32_t>(c[6]) << 16) |
+ (static_cast<uint32_t>(c[7]) << 24);
+ }
+
+ /* timespec */
+ bool operator==(const timespec& T) const {
+ return (tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
+ (tv_nsec == static_cast<uint32_t>(T.tv_nsec));
+ }
+ bool operator!=(const timespec& T) const {
+ return !(*this == T);
+ }
+ bool operator<(const timespec& T) const {
+ return (tv_sec < static_cast<uint32_t>(T.tv_sec)) ||
+ ((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
+ (tv_nsec < static_cast<uint32_t>(T.tv_nsec)));
+ }
+ bool operator>=(const timespec& T) const {
+ return !(*this < T);
+ }
+ bool operator>(const timespec& T) const {
+ return (tv_sec > static_cast<uint32_t>(T.tv_sec)) ||
+ ((tv_sec == static_cast<uint32_t>(T.tv_sec)) &&
+ (tv_nsec > static_cast<uint32_t>(T.tv_nsec)));
+ }
+ bool operator<=(const timespec& T) const {
+ return !(*this > T);
+ }
+
+ log_time operator-=(const timespec& T);
+ log_time operator-(const timespec& T) const {
+ log_time local(*this);
+ return local -= T;
+ }
+ log_time operator+=(const timespec& T);
+ log_time operator+(const timespec& T) const {
+ log_time local(*this);
+ return local += T;
+ }
+
+ /* log_time */
+ bool operator==(const log_time& T) const {
+ return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
+ }
+ bool operator!=(const log_time& T) const {
+ return !(*this == T);
+ }
+ bool operator<(const log_time& T) const {
+ return (tv_sec < T.tv_sec) ||
+ ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
+ }
+ bool operator>=(const log_time& T) const {
+ return !(*this < T);
+ }
+ bool operator>(const log_time& T) const {
+ return (tv_sec > T.tv_sec) ||
+ ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
+ }
+ bool operator<=(const log_time& T) const {
+ return !(*this > T);
+ }
+
+ log_time operator-=(const log_time& T);
+ log_time operator-(const log_time& T) const {
+ log_time local(*this);
+ return local -= T;
+ }
+ log_time operator+=(const log_time& T);
+ log_time operator+(const log_time& T) const {
+ log_time local(*this);
+ return local += T;
+ }
+
+ uint64_t nsec() const {
+ return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
+ }
+ uint64_t usec() const {
+ return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+ tv_nsec / (NS_PER_SEC / US_PER_SEC);
+ }
+ uint64_t msec() const {
+ return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+ tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+ }
+
+ static const char default_format[];
+
+ /* Add %#q for the fraction of a second to the standard library functions */
+ char* strptime(const char* s, const char* format = default_format);
+} __attribute__((__packed__));
+}
+
+#else /* __cplusplus */
+
+typedef struct log_time {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif /* __cplusplus */
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
new file mode 100644
index 000000000..7dfd914e4
--- /dev/null
+++ b/liblog/include/log/logprint.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android/log.h>
+#include <log/event_tag_map.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ /* Verbs */
+ FORMAT_OFF = 0,
+ FORMAT_BRIEF,
+ FORMAT_PROCESS,
+ FORMAT_TAG,
+ FORMAT_THREAD,
+ FORMAT_RAW,
+ FORMAT_TIME,
+ FORMAT_THREADTIME,
+ FORMAT_LONG,
+ /* Adverbs. The following are modifiers to above format verbs */
+ FORMAT_MODIFIER_COLOR, /* converts priority to color */
+ FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
+ FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
+ FORMAT_MODIFIER_YEAR, /* Adds year to date */
+ FORMAT_MODIFIER_ZONE, /* Adds zone to date, + UTC */
+ FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
+ FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
+ FORMAT_MODIFIER_UID, /* Adds uid */
+ FORMAT_MODIFIER_DESCRIPT, /* Adds descriptive */
+ /* private, undocumented */
+ FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
+} AndroidLogPrintFormat;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+ time_t tv_sec;
+ long tv_nsec;
+ android_LogPriority priority;
+ int32_t uid;
+ int32_t pid;
+ int32_t tid;
+ const char* tag;
+ size_t tagLen;
+ size_t messageLen;
+ const char* message;
+} AndroidLogEntry;
+
+AndroidLogFormat* android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat* p_format);
+
+/* currently returns 0 if format is a modifier, 1 if not */
+int android_log_setPrintFormat(AndroidLogFormat* p_format,
+ AndroidLogPrintFormat format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char* s);
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat* p_format,
+ const char* filterExpression);
+
+/**
+ * filterString: a whitespace-separated set of filter expressions
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat* p_format,
+ const char* filterString);
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
+ android_LogPriority pri);
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry* buf,
+ AndroidLogEntry* entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry* buf,
+ AndroidLogEntry* entry,
+ const EventTagMap* map, char* messageBuf,
+ int messageBufLen);
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry* p_line,
+ size_t* p_outLength);
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_printLogLine(AndroidLogFormat* p_format, int fd,
+ const AndroidLogEntry* entry);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
new file mode 100644
index 000000000..d3b72bcee
--- /dev/null
+++ b/liblog/include/private/android_logger.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is used to define the internal protocol for the Android Logger */
+
+#pragma once
+
+/* Android private interfaces */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+#include <string>
+#endif
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#define LOGGER_MAGIC 'l'
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Header Structure to pstore */
+typedef struct __attribute__((__packed__)) {
+ uint8_t magic;
+ uint16_t len;
+ uint16_t uid;
+ uint16_t pid;
+} android_pmsg_log_header_t;
+
+/* Header Structure to logd, and second header for pstore */
+typedef struct __attribute__((__packed__)) {
+ uint8_t id;
+ uint16_t tid;
+ log_time realtime;
+} android_log_header_t;
+
+/* Event Header Structure to logd */
+typedef struct __attribute__((__packed__)) {
+ int32_t tag; // Little Endian Order
+} android_event_header_t;
+
+// Event payload EVENT_TYPE_LIST
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_LIST
+ int8_t element_count;
+} android_event_list_t;
+
+// Event payload EVENT_TYPE_FLOAT
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_FLOAT
+ float data;
+} android_event_float_t;
+
+/* Event payload EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_INT
+ int32_t data; // Little Endian Order
+} android_event_int_t;
+
+/* Event with single EVENT_TYPE_INT */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ android_event_int_t payload;
+} android_log_event_int_t;
+
+/* Event payload EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_LONG
+ int64_t data; // Little Endian Order
+} android_event_long_t;
+
+/* Event with single EVENT_TYPE_LONG */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ android_event_long_t payload;
+} android_log_event_long_t;
+
+/*
+ * Event payload EVENT_TYPE_STRING
+ *
+ * Danger: do not embed this structure into another structure.
+ * This structure uses a flexible array member, and when
+ * compiled using g++, __builtin_object_size(data, 1) returns
+ * a bad value. This is possibly a g++ bug, or a bug due to
+ * the fact that flexible array members are not supported
+ * in C++.
+ * http://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c
+ */
+
+typedef struct __attribute__((__packed__)) {
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
+} android_event_string_t;
+
+/* Event with single EVENT_TYPE_STRING */
+typedef struct __attribute__((__packed__)) {
+ android_event_header_t header;
+ int8_t type; // EVENT_TYPE_STRING;
+ int32_t length; // Little Endian Order
+ char data[];
+} android_log_event_string_t;
+
+#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */
+#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000
+
+ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio,
+ const char* filename, const char* buf,
+ size_t len);
+
+#define LOG_ID_ANY ((log_id_t)-1)
+#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN
+
+/* first 5 arguments match __android_log_msg_file_write, a cast is safe */
+typedef ssize_t (*__android_log_pmsg_file_read_fn)(log_id_t logId, char prio,
+ const char* filename,
+ const char* buf, size_t len,
+ void* arg);
+
+ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio,
+ const char* prefix,
+ __android_log_pmsg_file_read_fn fn,
+ void* arg);
+
+int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len);
+int __android_log_security_bswrite(int32_t tag, const char* payload);
+int __android_log_security(); /* Device Owner is present */
+
+#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
+#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */
+#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */
+#define BOOL_DEFAULT_FLAG_PERSIST 0x2 /* <key>, persist.<key>, ro.<key> */
+#define BOOL_DEFAULT_FLAG_ENG 0x4 /* off for user */
+#define BOOL_DEFAULT_FLAG_SVELTE 0x8 /* off for low_ram */
+bool __android_logger_property_get_bool(const char* key, int flag);
+
+#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform \
+ */
+#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
+#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
+unsigned long __android_logger_get_buffer_size(log_id_t logId);
+bool __android_logger_valid_buffer_size(unsigned long value);
+
+/* Retrieve the composed event buffer */
+int android_log_write_list_buffer(android_log_context ctx, const char** msg);
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/liblog/include_vndk/android b/liblog/include_vndk/android
new file mode 120000
index 000000000..a3c0320f9
--- /dev/null
+++ b/liblog/include_vndk/android
@@ -0,0 +1 @@
+../include/android \ No newline at end of file
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
new file mode 100644
index 000000000..a79beecd4
--- /dev/null
+++ b/liblog/include_vndk/log/log.h
@@ -0,0 +1,25 @@
+/*Special log.h file for VNDK linking modules*/
+
+#ifndef _LIBS_LOG_LOG_H
+#define _LIBS_LOG_LOG_H
+
+#include <android/log.h>
+#include <log/log_id.h>
+#include <log/log_main.h>
+#include <log/log_radio.h>
+#include <log/log_read.h>
+#include <log/log_safetynet.h>
+#include <log/log_system.h>
+#include <log/log_time.h>
+
+/*
+ * LOG_TAG is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+#endif /*_LIBS_LOG_LOG_H*/
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
new file mode 100644
index 000000000..1f3dd377b
--- /dev/null
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2005-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Special log_event_list.h file for VNDK linking modules */
+
+#ifndef _LIBS_LOG_EVENT_LIST_H
+#define _LIBS_LOG_EVENT_LIST_H
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
+typedef struct android_log_context_internal* android_log_context;
+#endif
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ * elements, we will manufacturer a list to embrace it for your API
+ * convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+ size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/* Reset writer context */
+int android_log_reset(android_log_context ctx);
+
+/* Reset reader context */
+int android_log_parser_reset(android_log_context ctx,
+ const char* msg, size_t len);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include_vndk/log/log_id.h b/liblog/include_vndk/log/log_id.h
new file mode 120000
index 000000000..dce92b991
--- /dev/null
+++ b/liblog/include_vndk/log/log_id.h
@@ -0,0 +1 @@
+../../include/log/log_id.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_main.h b/liblog/include_vndk/log/log_main.h
new file mode 120000
index 000000000..f2ec01835
--- /dev/null
+++ b/liblog/include_vndk/log/log_main.h
@@ -0,0 +1 @@
+../../include/log/log_main.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_properties.h b/liblog/include_vndk/log/log_properties.h
new file mode 120000
index 000000000..bbec42614
--- /dev/null
+++ b/liblog/include_vndk/log/log_properties.h
@@ -0,0 +1 @@
+../../include/log/log_properties.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_radio.h b/liblog/include_vndk/log/log_radio.h
new file mode 120000
index 000000000..1e12b32d4
--- /dev/null
+++ b/liblog/include_vndk/log/log_radio.h
@@ -0,0 +1 @@
+../../include/log/log_radio.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_read.h b/liblog/include_vndk/log/log_read.h
new file mode 120000
index 000000000..01de8b9ec
--- /dev/null
+++ b/liblog/include_vndk/log/log_read.h
@@ -0,0 +1 @@
+../../include/log/log_read.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_safetynet.h b/liblog/include_vndk/log/log_safetynet.h
new file mode 120000
index 000000000..a4614e76c
--- /dev/null
+++ b/liblog/include_vndk/log/log_safetynet.h
@@ -0,0 +1 @@
+../../include/log/log_safetynet.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_system.h b/liblog/include_vndk/log/log_system.h
new file mode 120000
index 000000000..d0d390402
--- /dev/null
+++ b/liblog/include_vndk/log/log_system.h
@@ -0,0 +1 @@
+../../include/log/log_system.h \ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 100644
index 000000000..5a09959a7
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#ifndef NS_PER_SEC
+#define NS_PER_SEC 1000000000ULL
+#endif
+#ifndef US_PER_SEC
+#define US_PER_SEC 1000000ULL
+#endif
+#ifndef MS_PER_SEC
+#define MS_PER_SEC 1000ULL
+#endif
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+typedef struct log_time {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
new file mode 100644
index 000000000..6ca1a164d
--- /dev/null
+++ b/liblog/liblog.map.txt
@@ -0,0 +1,96 @@
+LIBLOG {
+ global:
+ android_name_to_log_id; # llndk
+ android_log_id_to_name; # llndk
+ __android_log_assert;
+ __android_log_buf_print;
+ __android_log_buf_write;
+ __android_log_print;
+ __android_log_vprint;
+ __android_log_write;
+ local:
+ *;
+};
+
+LIBLOG_L {
+ global:
+ android_logger_clear; # llndk
+ android_logger_get_id; # llndk
+ android_logger_get_log_readable_size; # llndk
+ android_logger_get_log_version; # llndk
+ android_logger_get_log_size; # llndk
+ android_logger_list_alloc; # apex llndk
+ android_logger_list_alloc_time; # apex llndk
+ android_logger_list_free; # apex llndk
+ android_logger_list_open; # llndk
+ android_logger_list_read; # apex llndk
+ android_logger_open; # apex llndk
+ android_logger_set_log_size; # llndk
+};
+
+LIBLOG_M {
+ global:
+ android_logger_get_prune_list; # llndk
+ android_logger_set_prune_list; # llndk
+ android_logger_get_statistics; # llndk
+ __android_log_error_write; # apex llndk
+ __android_log_is_loggable;
+ create_android_logger; # apex llndk
+ android_log_destroy; # apex llndk
+ android_log_write_list_begin; # apex llndk
+ android_log_write_list_end; # apex llndk
+ android_log_write_int32; # apex llndk
+ android_log_write_int64; # apex llndk
+ android_log_write_string8; # apex llndk
+ android_log_write_string8_len; # apex llndk
+ android_log_write_float32; # apex llndk
+ android_log_write_list; # apex llndk
+
+};
+
+LIBLOG_O {
+ global:
+ __android_log_is_loggable_len;
+ __android_log_is_debuggable; # apex llndk
+};
+
+LIBLOG_Q { # introduced=29
+ global:
+ __android_log_bswrite; # apex
+ __android_log_btwrite; # apex
+ __android_log_bwrite; # apex
+ __android_log_close; # apex
+ __android_log_security; # apex
+ android_log_reset; # llndk
+ android_log_parser_reset; # llndk
+};
+
+LIBLOG_R { # introduced=30
+ global:
+ __android_log_call_aborter;
+ __android_log_default_aborter;
+ __android_log_get_minimum_priority;
+ __android_log_logd_logger;
+ __android_log_security_bswrite; # apex
+ __android_log_set_aborter;
+ __android_log_set_default_tag;
+ __android_log_set_logger;
+ __android_log_set_minimum_priority;
+ __android_log_stderr_logger;
+ __android_log_write_log_message;
+};
+
+LIBLOG_PRIVATE {
+ global:
+ __android_log_pmsg_file_read;
+ __android_log_pmsg_file_write;
+ __android_logger_get_buffer_size;
+ __android_logger_property_get_bool;
+ android_openEventTagMap;
+ android_log_processBinaryLogBuffer;
+ android_log_processLogBuffer;
+ android_log_read_next;
+ android_log_write_list_buffer;
+ android_lookupEventTagNum;
+ create_android_log_parser;
+};
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
new file mode 100644
index 000000000..cb70d488a
--- /dev/null
+++ b/liblog/log_event_list.cpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log_event_list.h>
+#include <private/android_logger.h>
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+enum ReadWriteFlag {
+ kAndroidLoggerRead = 1,
+ kAndroidLoggerWrite = 2,
+};
+
+struct android_log_context_internal {
+ uint32_t tag;
+ unsigned pos; /* Read/write position into buffer */
+ unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
+ unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
+ unsigned list_nest_depth;
+ unsigned len; /* Length or raw buffer. */
+ bool overflow;
+ bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+ ReadWriteFlag read_write_flag;
+ uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+};
+
+static void init_context(android_log_context_internal* context, uint32_t tag) {
+ context->tag = tag;
+ context->read_write_flag = kAndroidLoggerWrite;
+ size_t needed = sizeof(android_event_list_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ }
+ /* Everything is a list */
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->list[0] = context->pos + 1;
+ context->pos += needed;
+}
+
+static void init_parser_context(android_log_context_internal* context, const char* msg,
+ size_t len) {
+ len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+ context->len = len;
+ memcpy(context->storage, msg, len);
+ context->read_write_flag = kAndroidLoggerRead;
+}
+
+android_log_context create_android_logger(uint32_t tag) {
+ android_log_context_internal* context;
+
+ context =
+ static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+ if (!context) {
+ return NULL;
+ }
+ init_context(context, tag);
+
+ return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char* msg, size_t len) {
+ android_log_context_internal* context;
+
+ context =
+ static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
+ if (!context) {
+ return NULL;
+ }
+ init_parser_context(context, msg, len);
+
+ return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context* ctx) {
+ android_log_context_internal* context;
+
+ context = (android_log_context_internal*)*ctx;
+ if (!context) {
+ return -EBADF;
+ }
+ memset(context, 0, sizeof(*context));
+ free(context);
+ *ctx = NULL;
+ return 0;
+}
+
+int android_log_reset(android_log_context context) {
+ uint32_t tag;
+
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ tag = context->tag;
+ memset(context, 0, sizeof(*context));
+ init_context(context, tag);
+
+ return 0;
+}
+
+int android_log_parser_reset(android_log_context context, const char* msg, size_t len) {
+ if (!context || (kAndroidLoggerRead != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ memset(context, 0, sizeof(*context));
+ init_parser_context(context, msg, len);
+
+ return 0;
+}
+
+int android_log_write_list_begin(android_log_context context) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ size_t needed = sizeof(android_event_list_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->list_nest_depth++;
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[context->pos]);
+ event_list->type = EVENT_TYPE_LIST;
+ event_list->element_count = 0;
+ context->list[context->list_nest_depth] = context->pos + 1;
+ context->count[context->list_nest_depth] = 0;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_int32(android_log_context context, int32_t value) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ size_t needed = sizeof(android_event_int_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
+ event_int->type = EVENT_TYPE_INT;
+ event_int->data = value;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_int64(android_log_context context, int64_t value) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ size_t needed = sizeof(android_event_long_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
+ event_long->type = EVENT_TYPE_LONG;
+ event_long->data = value;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_string8_len(android_log_context context, const char* value, size_t maxlen) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ if (!value) {
+ value = "";
+ }
+ int32_t len = strnlen(value, maxlen);
+ size_t needed = sizeof(android_event_string_t) + len;
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ /* Truncate string for delivery */
+ len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+ if (len <= 0) {
+ context->overflow = true;
+ return -EIO;
+ }
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
+ event_string->type = EVENT_TYPE_STRING;
+ event_string->length = len;
+ if (len) {
+ memcpy(&event_string->data, value, len);
+ }
+ context->pos += needed;
+ return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char* value) {
+ return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context context, float value) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ size_t needed = sizeof(android_event_float_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
+ event_float->type = EVENT_TYPE_FLOAT;
+ event_float->data = value;
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_list_end(android_log_context context) {
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ context->list_nest_depth--;
+ return -EOVERFLOW;
+ }
+ if (!context->list_nest_depth) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->list[context->list_nest_depth] <= 0) {
+ context->list_nest_depth--;
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ context->storage[context->list[context->list_nest_depth]] =
+ context->count[context->list_nest_depth];
+ context->list_nest_depth--;
+ return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context context, log_id_t id) {
+ const char* msg;
+ ssize_t len;
+
+ if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
+ return -EINVAL;
+ }
+
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+ return (id == LOG_ID_EVENTS)
+ ? __android_log_bwrite(context->tag, msg, len)
+ : ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
+ : __android_log_security_bwrite(context->tag, msg, len));
+}
+
+int android_log_write_list_buffer(android_log_context context, const char** buffer) {
+ const char* msg;
+ ssize_t len;
+
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+ if (buffer == NULL) {
+ return -EFAULT;
+ }
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+ *buffer = msg;
+ return len;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(android_log_context context,
+ int peek) {
+ android_log_list_element elem;
+ unsigned pos;
+
+ memset(&elem, 0, sizeof(elem));
+
+ /* Nothing to parse from this context, so return complete. */
+ if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+ (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+ (context->count[context->list_nest_depth] >=
+ (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ if (context &&
+ (context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+ !context->count[context->list_nest_depth]))) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ }
+ elem.complete = true;
+ return elem;
+ }
+
+ /*
+ * Use a different variable to update the position in case this
+ * operation is a "peek".
+ */
+ pos = context->pos;
+ if (context->list_stop) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ elem.complete = !context->count[0] && (!context->list_nest_depth ||
+ ((context->list_nest_depth == 1) && !context->count[1]));
+ if (!peek) {
+ /* Suck in superfluous stop */
+ if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+ context->pos = pos + 1;
+ }
+ if (context->list_nest_depth) {
+ --context->list_nest_depth;
+ if (context->count[context->list_nest_depth]) {
+ context->list_stop = false;
+ }
+ } else {
+ context->list_stop = false;
+ }
+ }
+ return elem;
+ }
+ if ((pos + 1) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+
+ elem.type = static_cast<AndroidEventLogType>(context->storage[pos]);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_FLOAT:
+ /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+ /* FALLTHRU */
+ case EVENT_TYPE_INT: {
+ elem.len = sizeof(int32_t);
+ if ((pos + sizeof(android_event_int_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+
+ auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_int_t);
+ elem.data.int32 = event_int->data;
+ /* common tangeable object suffix */
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+ }
+
+ case EVENT_TYPE_LONG: {
+ elem.len = sizeof(int64_t);
+ if ((pos + sizeof(android_event_long_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+
+ auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_long_t);
+ elem.data.int64 = event_long->data;
+ /* common tangeable object suffix */
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+ }
+
+ case EVENT_TYPE_STRING: {
+ if ((pos + sizeof(android_event_string_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_string_t);
+ // Wire format is int32_t, but elem.len is uint16_t...
+ if (event_string->length >= UINT16_MAX) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.len = event_string->length;
+ if ((pos + elem.len) > context->len) {
+ elem.len = context->len - pos; /* truncate string */
+ elem.complete = true;
+ if (!elem.len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ }
+ elem.data.string = event_string->data;
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+ }
+
+ case EVENT_TYPE_LIST: {
+ if ((pos + sizeof(android_event_list_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[pos]);
+ pos += sizeof(android_event_list_t);
+ elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+ if (peek) {
+ return elem;
+ }
+ if (context->count[context->list_nest_depth]) {
+ context->count[context->list_nest_depth]--;
+ }
+ context->list_stop = event_list->element_count == 0;
+ context->list_nest_depth++;
+ if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->count[context->list_nest_depth] = event_list->element_count;
+ }
+ context->pos = pos;
+ return elem;
+ }
+
+ case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+ pos++;
+ if (!peek) {
+ context->pos = pos;
+ }
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = !context->list_nest_depth;
+ if (context->list_nest_depth > 0) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ if (!peek) {
+ context->list_nest_depth--;
+ }
+ }
+ return elem;
+
+ default:
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.cpp b/liblog/log_event_write.cpp
new file mode 100644
index 000000000..39afd0c72
--- /dev/null
+++ b/liblog/log_event_write.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#define MAX_SUBTAG_LEN 32
+
+int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
+ uint32_t dataLen) {
+ int ret = -EINVAL;
+
+ if (subTag && (data || !dataLen)) {
+ android_log_context ctx = create_android_logger(tag);
+
+ ret = -ENOMEM;
+ if (ctx) {
+ ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+ if (ret >= 0) {
+ ret = android_log_write_int32(ctx, uid);
+ if (ret >= 0) {
+ ret = android_log_write_string8_len(ctx, data, dataLen);
+ if (ret >= 0) {
+ ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+ }
+ }
+ }
+ android_log_destroy(&ctx);
+ }
+ }
+ return ret;
+}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
new file mode 100644
index 000000000..3fbe1cbc5
--- /dev/null
+++ b/liblog/log_time.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <private/android_logger.h>
+
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+const timespec log_time::EPOCH = {0, 0};
+
+// Add %#q for fractional seconds to standard strptime function
+
+char* log_time::strptime(const char* s, const char* format) {
+ time_t now;
+#ifdef __linux__
+ *this = log_time(CLOCK_REALTIME);
+ now = tv_sec;
+#else
+ time(&now);
+ tv_sec = now;
+ tv_nsec = 0;
+#endif
+
+ struct tm* ptm;
+#if !defined(_WIN32)
+ struct tm tmBuf;
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+
+ char fmt[strlen(format) + 1];
+ strcpy(fmt, format);
+
+ char* ret = const_cast<char*>(s);
+ char* cp;
+ for (char* f = cp = fmt;; ++cp) {
+ if (!*cp) {
+ if (f != cp) {
+ ret = ::strptime(ret, f, ptm);
+ }
+ break;
+ }
+ if (*cp != '%') {
+ continue;
+ }
+ char* e = cp;
+ ++e;
+#if (defined(__BIONIC__))
+ if (*e == 's') {
+ *cp = '\0';
+ if (*f) {
+ ret = ::strptime(ret, f, ptm);
+ if (!ret) {
+ break;
+ }
+ }
+ tv_sec = 0;
+ while (isdigit(*ret)) {
+ tv_sec = tv_sec * 10 + *ret - '0';
+ ++ret;
+ }
+ now = tv_sec;
+#if !defined(_WIN32)
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+ } else
+#endif
+ {
+ unsigned num = 0;
+ while (isdigit(*e)) {
+ num = num * 10 + *e - '0';
+ ++e;
+ }
+ if (*e != 'q') {
+ continue;
+ }
+ *cp = '\0';
+ if (*f) {
+ ret = ::strptime(ret, f, ptm);
+ if (!ret) {
+ break;
+ }
+ }
+ unsigned long mul = NS_PER_SEC;
+ if (num == 0) {
+ num = INT_MAX;
+ }
+ tv_nsec = 0;
+ while (isdigit(*ret) && num && (mul > 1)) {
+ --num;
+ mul /= 10;
+ tv_nsec = tv_nsec + (*ret - '0') * mul;
+ ++ret;
+ }
+ }
+ f = cp = e;
+ ++f;
+ }
+
+ if (ret) {
+ tv_sec = mktime(ptm);
+ return ret;
+ }
+
+// Upon error, place a known value into the class, the current time.
+#ifdef __linux__
+ *this = log_time(CLOCK_REALTIME);
+#else
+ time(&now);
+ tv_sec = now;
+ tv_nsec = 0;
+#endif
+ return ret;
+}
+
+log_time log_time::operator-=(const timespec& T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = log_time(EPOCH);
+ }
+
+ if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator+=(const timespec& T) {
+ this->tv_nsec += (unsigned long int)T.tv_nsec;
+ if (this->tv_nsec >= NS_PER_SEC) {
+ this->tv_nsec -= NS_PER_SEC;
+ ++this->tv_sec;
+ }
+ this->tv_sec += T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator-=(const log_time& T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = log_time(EPOCH);
+ }
+
+ if (this->tv_nsec < T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator+=(const log_time& T) {
+ this->tv_nsec += T.tv_nsec;
+ if (this->tv_nsec >= NS_PER_SEC) {
+ this->tv_nsec -= NS_PER_SEC;
+ ++this->tv_sec;
+ }
+ this->tv_sec += T.tv_sec;
+
+ return *this;
+}
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
new file mode 100644
index 000000000..82ed6b2b5
--- /dev/null
+++ b/liblog/logd_reader.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logd_reader.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <private/android_logger.h>
+
+#include "logger.h"
+
+// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
+// O_CLOEXEC is always set.
+static int socket_local_client(const std::string& name, int type) {
+ sockaddr_un addr = {.sun_family = AF_LOCAL};
+
+ std::string path = "/dev/socket/" + name;
+ if (path.size() + 1 > sizeof(addr.sun_path)) {
+ return -1;
+ }
+ strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+ int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
+ if (fd == -1) {
+ return -1;
+ }
+
+ if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* worker for sending the command to the logger */
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
+ ssize_t ret;
+ size_t len;
+ char* cp;
+ int errno_save = 0;
+ int sock = socket_local_client("logd", SOCK_STREAM);
+ if (sock < 0) {
+ return sock;
+ }
+
+ len = strlen(buf) + 1;
+ ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
+ if (ret <= 0) {
+ goto done;
+ }
+
+ len = buf_size;
+ cp = buf;
+ while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+ struct pollfd p;
+
+ if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+ break;
+ }
+
+ len -= ret;
+ cp += ret;
+
+ memset(&p, 0, sizeof(p));
+ p.fd = sock;
+ p.events = POLLIN;
+
+ /* Give other side 20ms to refill pipe */
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+ if (ret <= 0) {
+ break;
+ }
+
+ if (!(p.revents & POLLIN)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret >= 0) {
+ ret += buf_size - len;
+ }
+
+done:
+ if ((ret == -1) && errno) {
+ errno_save = errno;
+ }
+ close(sock);
+ if (errno_save) {
+ errno = errno_save;
+ }
+ return ret;
+}
+
+static int check_log_success(char* buf, ssize_t ret) {
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (strncmp(buf, "success", 7)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int android_logger_clear(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
+
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
+}
+
+/* returns the total size of the log's ring buffer */
+long android_logger_get_log_size(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+
+ ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((buf[0] < '0') || ('9' < buf[0])) {
+ return -1;
+ }
+
+ return atol(buf);
+}
+
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
+
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+long android_logger_get_log_readable_size(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+
+ ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((buf[0] < '0') || ('9' < buf[0])) {
+ return -1;
+ }
+
+ return atol(buf);
+}
+
+int android_logger_get_log_version(struct logger*) {
+ return 4;
+}
+
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
+ char* cp = buf;
+ size_t remaining = len;
+ size_t n;
+
+ n = snprintf(cp, remaining, "getStatistics");
+ n = MIN(n, remaining);
+ remaining -= n;
+ cp += n;
+
+ for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+ if ((1 << log_id) & logger_list->log_mask) {
+ n = snprintf(cp, remaining, " %zu", log_id);
+ n = MIN(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
+ }
+
+ if (logger_list->pid) {
+ snprintf(cp, remaining, " pid=%u", logger_list->pid);
+ }
+
+ return SendLogdControlMessage(buf, len);
+}
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
+ snprintf(buf, len, "getPruneList");
+ return SendLogdControlMessage(buf, len);
+}
+
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
+ std::string cmd = "setPruneList " + std::string{buf, len};
+
+ return check_log_success(cmd.data(), SendLogdControlMessage(cmd.data(), cmd.size()));
+}
+
+static int logdOpen(struct logger_list* logger_list) {
+ char buffer[256], *cp, c;
+ int ret, remaining, sock;
+
+ sock = atomic_load(&logger_list->fd);
+ if (sock > 0) {
+ return sock;
+ }
+
+ sock = socket_local_client("logdr", SOCK_SEQPACKET);
+ if (sock <= 0) {
+ if ((sock == -1) && errno) {
+ return -errno;
+ }
+ return sock;
+ }
+
+ strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
+ cp = buffer + strlen(buffer);
+
+ strcpy(cp, " lids");
+ cp += 5;
+ c = '=';
+ remaining = sizeof(buffer) - (cp - buffer);
+
+ for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+ if ((1 << log_id) & logger_list->log_mask) {
+ ret = snprintf(cp, remaining, "%c%zu", c, log_id);
+ ret = MIN(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ c = ',';
+ }
+ }
+
+ if (logger_list->tail) {
+ ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+ ret = MIN(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+ if (logger_list->mode & ANDROID_LOG_WRAP) {
+ // ToDo: alternate API to allow timeout to be adjusted.
+ ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+ ret = MIN(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+ ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
+ logger_list->start.tv_nsec);
+ ret = MIN(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
+ if (logger_list->pid) {
+ ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+ ret = MIN(ret, remaining);
+ cp += ret;
+ }
+
+ ret = TEMP_FAILURE_RETRY(write(sock, buffer, cp - buffer));
+ int write_errno = errno;
+
+ if (ret <= 0) {
+ close(sock);
+ if (ret == -1) {
+ return -write_errno;
+ }
+ if (ret == 0) {
+ return -EIO;
+ }
+ return ret;
+ }
+
+ ret = atomic_exchange(&logger_list->fd, sock);
+ if ((ret > 0) && (ret != sock)) {
+ close(ret);
+ }
+ return sock;
+}
+
+/* Read from the selected logs */
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg) {
+ int ret = logdOpen(logger_list);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
+ ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
+ if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
+ return -EAGAIN;
+ }
+
+ if (ret == -1) {
+ return -errno;
+ }
+ return ret;
+}
+
+/* Close all the logs */
+void LogdClose(struct logger_list* logger_list) {
+ int sock = atomic_exchange(&logger_list->fd, -1);
+ if (sock > 0) {
+ close(sock);
+ }
+}
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
new file mode 100644
index 000000000..68eef027a
--- /dev/null
+++ b/liblog/logd_reader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include "log/log_read.h"
+
+__BEGIN_DECLS
+
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void LogdClose(struct logger_list* logger_list);
+
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
+
+__END_DECLS
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
new file mode 100644
index 000000000..a230749e1
--- /dev/null
+++ b/liblog/logd_writer.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logd_writer.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+#include "uio.h"
+
+static atomic_int logd_socket;
+
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
+ sockaddr_un un = {};
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, "/dev/socket/logdw");
+ TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
+}
+
+// logd_socket should only be opened once. If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket. If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+ if (logd_socket != 0) {
+ return;
+ }
+
+ int new_socket =
+ TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (new_socket <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+ close(new_socket);
+ return;
+ }
+
+ LogdConnect();
+}
+
+// This is the one exception to the above. Zygote uses this to clean up open FD's after fork() and
+// before specialization. It is single threaded at this point and therefore this function is
+// explicitly not thread safe. It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
+void LogdClose() {
+ if (logd_socket > 0) {
+ close(logd_socket);
+ }
+ logd_socket = 0;
+}
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+ ssize_t ret;
+ static const unsigned headerLength = 1;
+ struct iovec newVec[nr + headerLength];
+ android_log_header_t header;
+ size_t i, payloadSize;
+ static atomic_int dropped;
+ static atomic_int droppedSecurity;
+
+ GetSocket();
+
+ if (logd_socket <= 0) {
+ return -EBADF;
+ }
+
+ /* logd, after initialization and priv drop */
+ if (getuid() == AID_LOGD) {
+ /*
+ * ignore log messages we send to ourself (logd).
+ * Such log messages are often generated by libraries we depend on
+ * which use standard Android logging.
+ */
+ return 0;
+ }
+
+ header.tid = gettid();
+ header.realtime.tv_sec = ts->tv_sec;
+ header.realtime.tv_nsec = ts->tv_nsec;
+
+ newVec[0].iov_base = (unsigned char*)&header;
+ newVec[0].iov_len = sizeof(header);
+
+ int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_SECURITY;
+ buffer.header.tag = LIBLOG_LOG_TAG;
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = snapshot;
+
+ newVec[headerLength].iov_base = &buffer;
+ newVec[headerLength].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
+ }
+ }
+ snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+ ANDROID_LOG_VERBOSE)) {
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_EVENTS;
+ buffer.header.tag = LIBLOG_LOG_TAG;
+ buffer.payload.type = EVENT_TYPE_INT;
+ buffer.payload.data = snapshot;
+
+ newVec[headerLength].iov_base = &buffer;
+ newVec[headerLength].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ }
+ }
+
+ header.id = logId;
+
+ for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+ newVec[i].iov_base = vec[i - headerLength].iov_base;
+ payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+ if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+ newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+ if (newVec[i].iov_len) {
+ ++i;
+ }
+ break;
+ }
+ }
+
+ // The write below could be lost, but will never block.
+ // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
+ // the connection, so we reset it and try again.
+ ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
+ if (ret < 0 && errno != EAGAIN) {
+ LogdConnect();
+
+ ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
+ }
+
+ if (ret < 0) {
+ ret = -errno;
+ }
+
+ if (ret > (ssize_t)sizeof(header)) {
+ ret -= sizeof(header);
+ } else if (ret < 0) {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ if (logId == LOG_ID_SECURITY) {
+ atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
+ }
+ }
+
+ return ret;
+}
diff --git a/liblog/logd_writer.h b/liblog/logd_writer.h
new file mode 100644
index 000000000..41197b595
--- /dev/null
+++ b/liblog/logd_writer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#include <android/log.h>
+
+int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void LogdClose();
diff --git a/liblog/logger.h b/liblog/logger.h
new file mode 100644
index 000000000..ddff19dd5
--- /dev/null
+++ b/liblog/logger.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdatomic.h>
+#include <sys/cdefs.h>
+
+#include <log/log.h>
+
+#include "uio.h"
+
+__BEGIN_DECLS
+
+struct logger_list {
+ atomic_int fd;
+ int mode;
+ unsigned int tail;
+ log_time start;
+ pid_t pid;
+ uint32_t log_mask;
+};
+
+// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
+// bit 31: Set if this 'logger' is for logd.
+// bit 30: Set if this 'logger' is for pmsg
+// bits 0-2: the decimal value of the log buffer.
+// Other bits are unused.
+
+#define LOGGER_LOGD (1U << 31)
+#define LOGGER_PMSG (1U << 30)
+#define LOGGER_LOG_ID_MASK ((1U << 3) - 1)
+
+inline bool android_logger_is_logd(struct logger* logger) {
+ return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
+}
+
+__END_DECLS
diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp
new file mode 100644
index 000000000..e72290ed1
--- /dev/null
+++ b/liblog/logger_name.cpp
@@ -0,0 +1,72 @@
+/*
+** Copyright 2013-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <string.h>
+#include <type_traits>
+
+#include <log/log.h>
+
+/* In the future, we would like to make this list extensible */
+static const char* LOG_NAME[LOG_ID_MAX] = {
+ /* clang-format off */
+ [LOG_ID_MAIN] = "main",
+ [LOG_ID_RADIO] = "radio",
+ [LOG_ID_EVENTS] = "events",
+ [LOG_ID_SYSTEM] = "system",
+ [LOG_ID_CRASH] = "crash",
+ [LOG_ID_STATS] = "stats",
+ [LOG_ID_SECURITY] = "security",
+ [LOG_ID_KERNEL] = "kernel",
+ /* clang-format on */
+};
+
+const char* android_log_id_to_name(log_id_t log_id) {
+ if (log_id >= LOG_ID_MAX) {
+ log_id = LOG_ID_MAIN;
+ }
+ return LOG_NAME[log_id];
+}
+
+static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
+ "log_id_t must be an uint32_t");
+
+static_assert(std::is_same<std::underlying_type<android_LogPriority>::type, uint32_t>::value,
+ "log_id_t must be an uint32_t");
+
+log_id_t android_name_to_log_id(const char* logName) {
+ const char* b;
+ unsigned int ret;
+
+ if (!logName) {
+ return static_cast<log_id_t>(LOG_ID_MAX);
+ }
+
+ b = strrchr(logName, '/');
+ if (!b) {
+ b = logName;
+ } else {
+ ++b;
+ }
+
+ for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+ const char* l = LOG_NAME[ret];
+ if (l && !strcmp(b, l)) {
+ return static_cast<log_id_t>(ret);
+ }
+ }
+
+ return static_cast<log_id_t>(LOG_ID_MAX);
+}
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
new file mode 100644
index 000000000..4937042eb
--- /dev/null
+++ b/liblog/logger_read.cpp
@@ -0,0 +1,149 @@
+/*
+** Copyright 2013-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "log/log_read.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android/log.h>
+
+#include "logd_reader.h"
+#include "logger.h"
+#include "pmsg_reader.h"
+
+/* method for getting the associated sublog id */
+log_id_t android_logger_get_id(struct logger* logger) {
+ return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
+}
+
+static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
+ log_time start, pid_t pid) {
+ auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
+ if (!logger_list) {
+ return nullptr;
+ }
+
+ logger_list->mode = mode;
+ logger_list->start = start;
+ logger_list->tail = tail;
+ logger_list->pid = pid;
+
+ return logger_list;
+}
+
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
+ return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
+}
+
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
+ return android_logger_list_alloc_internal(mode, 0, start, pid);
+}
+
+/* Open the named log and add it to the logger list */
+struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
+ if (!logger_list || (logId >= LOG_ID_MAX)) {
+ return nullptr;
+ }
+
+ logger_list->log_mask |= 1 << logId;
+
+ uintptr_t logger = logId;
+ logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
+ return reinterpret_cast<struct logger*>(logger);
+}
+
+/* Open the single named log and make it part of a new logger list */
+struct logger_list* android_logger_list_open(log_id_t logId, int mode, unsigned int tail,
+ pid_t pid) {
+ struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
+
+ if (!logger_list) {
+ return NULL;
+ }
+
+ if (!android_logger_open(logger_list, logId)) {
+ android_logger_list_free(logger_list);
+ return NULL;
+ }
+
+ return logger_list;
+}
+
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+ if (logger_list == nullptr || logger_list->log_mask == 0) {
+ return -EINVAL;
+ }
+
+ int ret = 0;
+
+#ifdef __ANDROID__
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ ret = PmsgRead(logger_list, log_msg);
+ } else {
+ ret = LogdRead(logger_list, log_msg);
+ }
+#endif
+
+ if (ret <= 0) {
+ return ret;
+ }
+
+ if (ret > LOGGER_ENTRY_MAX_LEN) {
+ ret = LOGGER_ENTRY_MAX_LEN;
+ }
+
+ if (ret < static_cast<int>(sizeof(log_msg->entry))) {
+ return -EINVAL;
+ }
+
+ if (log_msg->entry.hdr_size < sizeof(log_msg->entry) ||
+ log_msg->entry.hdr_size >= LOGGER_ENTRY_MAX_LEN - sizeof(log_msg->entry)) {
+ return -EINVAL;
+ }
+
+ if (log_msg->entry.len > ret - log_msg->entry.hdr_size) {
+ return -EINVAL;
+ }
+
+ log_msg->buf[log_msg->entry.len + log_msg->entry.hdr_size] = '\0';
+
+ return ret;
+}
+
+/* Close all the logs */
+void android_logger_list_free(struct logger_list* logger_list) {
+ if (logger_list == NULL) {
+ return;
+ }
+
+#ifdef __ANDROID__
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ PmsgClose(logger_list);
+ } else {
+ LogdClose(logger_list);
+ }
+#endif
+
+ free(logger_list);
+}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
new file mode 100644
index 000000000..b1bed80e9
--- /dev/null
+++ b/liblog/logger_write.cpp
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logger_write.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#ifdef __BIONIC__
+#include <android/set_abort_message.h>
+#endif
+
+#include <atomic>
+
+#include <android-base/errno_restorer.h>
+#include <android-base/macros.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "android/log.h"
+#include "log/log_read.h"
+#include "logger.h"
+#include "uio.h"
+
+#ifdef __ANDROID__
+#include "logd_writer.h"
+#include "pmsg_writer.h"
+#endif
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+using android::base::ErrnoRestorer;
+
+#define LOG_BUF_SIZE 1024
+
+#if defined(__ANDROID__)
+static int check_log_uid_permissions() {
+ uid_t uid = getuid();
+
+ /* Matches clientHasLogCredentials() in logd */
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+ uid = geteuid();
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+ gid_t gid = getgid();
+ if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+ gid = getegid();
+ if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+ int num_groups;
+ gid_t* groups;
+
+ num_groups = getgroups(0, NULL);
+ if (num_groups <= 0) {
+ return -EPERM;
+ }
+ groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
+ if (!groups) {
+ return -ENOMEM;
+ }
+ num_groups = getgroups(num_groups, groups);
+ while (num_groups > 0) {
+ if (groups[num_groups - 1] == AID_LOG) {
+ break;
+ }
+ --num_groups;
+ }
+ free(groups);
+ if (num_groups <= 0) {
+ return -EPERM;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+/*
+ * Release any logger resources. A new log write will immediately re-acquire.
+ */
+void __android_log_close() {
+#ifdef __ANDROID__
+ LogdClose();
+ PmsgClose();
+#endif
+}
+
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
+#if defined(__GLIBC__)
+ return program_invocation_short_name;
+#elif defined(_WIN32)
+ static bool first = true;
+ static char progname[MAX_PATH] = {};
+
+ if (first) {
+ char path[PATH_MAX + 1];
+ DWORD result = GetModuleFileName(nullptr, path, sizeof(path) - 1);
+ if (result == 0 || result == sizeof(path) - 1) return "";
+ path[PATH_MAX - 1] = 0;
+
+ char* path_basename = basename(path);
+
+ snprintf(progname, sizeof(progname), "%s", path_basename);
+ first = false;
+ }
+
+ return progname;
+#endif
+}
+#endif
+
+// It's possible for logging to happen during static initialization before our globals are
+// initialized, so we place this std::string in a function such that it is initialized on the first
+// call.
+std::string& GetDefaultTag() {
+ static std::string default_tag = getprogname();
+ return default_tag;
+}
+
+void __android_log_set_default_tag(const char* tag) {
+ GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
+}
+
+static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT;
+int32_t __android_log_set_minimum_priority(int32_t priority) {
+ return minimum_log_priority.exchange(priority, std::memory_order_relaxed);
+}
+
+int32_t __android_log_get_minimum_priority() {
+ return minimum_log_priority;
+}
+
+#ifdef __ANDROID__
+static __android_logger_function logger_function = __android_log_logd_logger;
+#else
+static __android_logger_function logger_function = __android_log_stderr_logger;
+#endif
+
+void __android_log_set_logger(__android_logger_function logger) {
+ logger_function = logger;
+}
+
+void __android_log_default_aborter(const char* abort_message) {
+#ifdef __ANDROID__
+ android_set_abort_message(abort_message);
+#else
+ UNUSED(abort_message);
+#endif
+ abort();
+}
+
+static __android_aborter_function aborter_function = __android_log_default_aborter;
+
+void __android_log_set_aborter(__android_aborter_function aborter) {
+ aborter_function = aborter;
+}
+
+void __android_log_call_aborter(const char* abort_message) {
+ aborter_function(abort_message);
+}
+
+#ifdef __ANDROID__
+static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) {
+ int ret;
+ struct timespec ts;
+
+ if (log_id == LOG_ID_KERNEL) {
+ return -EINVAL;
+ }
+
+ clock_gettime(android_log_clockid(), &ts);
+
+ if (log_id == LOG_ID_SECURITY) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+
+ ret = check_log_uid_permissions();
+ if (ret < 0) {
+ return ret;
+ }
+ if (!__android_log_security()) {
+ /* If only we could reset downstream logd counter */
+ return -EPERM;
+ }
+ } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+ }
+
+ ret = LogdWrite(log_id, &ts, vec, nr);
+ PmsgWrite(log_id, &ts, vec, nr);
+
+ return ret;
+}
+#else
+static int write_to_log(log_id_t, struct iovec*, size_t) {
+ // Non-Android text logs should go to __android_log_stderr_logger, not here.
+ // Non-Android binary logs are always dropped.
+ return 1;
+}
+#endif
+
+// Copied from base/threads.cpp
+static uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+ return gettid();
+#elif defined(__APPLE__)
+ uint64_t tid;
+ pthread_threadid_np(NULL, &tid);
+ return tid;
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+
+void __android_log_stderr_logger(const struct __android_log_message* log_message) {
+ struct tm now;
+ time_t t = time(nullptr);
+
+#if defined(_WIN32)
+ localtime_s(&now, &t);
+#else
+ localtime_r(&t, &now);
+#endif
+
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+ static const char log_characters[] = "XXVDIWEF";
+ static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+ "Mismatch in size of log_characters and values in android_LogPriority");
+ int32_t priority =
+ log_message->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : log_message->priority;
+ char priority_char = log_characters[priority];
+ uint64_t tid = GetThreadId();
+
+ if (log_message->file != nullptr) {
+ fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n",
+ log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
+ tid, log_message->file, log_message->line, log_message->message);
+ } else {
+ fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n",
+ log_message->tag ? log_message->tag : "nullptr", priority_char, timestamp, getpid(),
+ tid, log_message->message);
+ }
+}
+
+void __android_log_logd_logger(const struct __android_log_message* log_message) {
+ int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id;
+
+ struct iovec vec[3];
+ vec[0].iov_base =
+ const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority));
+ vec[0].iov_len = 1;
+ vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag));
+ vec[1].iov_len = strlen(log_message->tag) + 1;
+ vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message));
+ vec[2].iov_len = strlen(log_message->message) + 1;
+
+ write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
+}
+
+int __android_log_write(int prio, const char* tag, const char* msg) {
+ return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+void __android_log_write_log_message(__android_log_message* log_message) {
+ ErrnoRestorer errno_restorer;
+
+ if (log_message->buffer_id != LOG_ID_DEFAULT && log_message->buffer_id != LOG_ID_MAIN &&
+ log_message->buffer_id != LOG_ID_SYSTEM && log_message->buffer_id != LOG_ID_RADIO &&
+ log_message->buffer_id != LOG_ID_CRASH) {
+ return;
+ }
+
+ if (log_message->tag == nullptr) {
+ log_message->tag = GetDefaultTag().c_str();
+ }
+
+#if __BIONIC__
+ if (log_message->priority == ANDROID_LOG_FATAL) {
+ android_set_abort_message(log_message->message);
+ }
+#endif
+
+ logger_function(log_message);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+ ErrnoRestorer errno_restorer;
+
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return -EPERM;
+ }
+
+ __android_log_message log_message = {
+ sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, msg};
+ __android_log_write_log_message(&log_message);
+ return 1;
+}
+
+int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+ ErrnoRestorer errno_restorer;
+
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return -EPERM;
+ }
+
+ char buf[LOG_BUF_SIZE];
+
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+ __android_log_message log_message = {
+ sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
+ __android_log_write_log_message(&log_message);
+ return 1;
+}
+
+int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ ErrnoRestorer errno_restorer;
+
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return -EPERM;
+ }
+
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ __android_log_message log_message = {
+ sizeof(__android_log_message), LOG_ID_MAIN, prio, tag, nullptr, 0, buf};
+ __android_log_write_log_message(&log_message);
+ return 1;
+}
+
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+ ErrnoRestorer errno_restorer;
+
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+ return -EPERM;
+ }
+
+ va_list ap;
+ char buf[LOG_BUF_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+
+ __android_log_message log_message = {
+ sizeof(__android_log_message), bufID, prio, tag, nullptr, 0, buf};
+ __android_log_write_log_message(&log_message);
+ return 1;
+}
+
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
+ char buf[LOG_BUF_SIZE];
+
+ if (fmt) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+ va_end(ap);
+ } else {
+ /* Msg not provided, log condition. N.B. Do not use cond directly as
+ * format string as it could contain spurious '%' syntax (e.g.
+ * "%d" in "blocks%devs == 0").
+ */
+ if (cond)
+ snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+ else
+ strcpy(buf, "Unspecified assertion failed");
+ }
+
+ // Log assertion failures to stderr for the benefit of "adb shell" users
+ // and gtests (http://b/23675822).
+ TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
+ TEMP_FAILURE_RETRY(write(2, "\n", 1));
+
+ __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+ __android_log_call_aborter(buf);
+ abort();
+}
+
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
+ ErrnoRestorer errno_restorer;
+
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
+ ErrnoRestorer errno_restorer;
+
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_STATS, vec, 2);
+}
+
+int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
+ ErrnoRestorer errno_restorer;
+
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well. Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
+ ErrnoRestorer errno_restorer;
+
+ struct iovec vec[3];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = (void*)payload;
+ vec[2].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char* payload) {
+ ErrnoRestorer errno_restorer;
+
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+int __android_log_security_bswrite(int32_t tag, const char* payload) {
+ ErrnoRestorer errno_restorer;
+
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 4);
+}
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
new file mode 100644
index 000000000..eee277821
--- /dev/null
+++ b/liblog/logger_write.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+std::string& GetDefaultTag();
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
new file mode 100644
index 000000000..5c69bf863
--- /dev/null
+++ b/liblog/logprint.cpp
@@ -0,0 +1,1750 @@
+/*
+**
+** Copyright 2006-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef __MINGW32__
+#define HAVE_STRSEP
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#ifndef __MINGW32__
+#include <pwd.h>
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <wchar.h>
+
+#include <cutils/list.h>
+
+#include <log/log.h>
+#include <log/logprint.h>
+#include <private/android_logger.h>
+
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+typedef struct FilterInfo_t {
+ char* mTag;
+ android_LogPriority mPri;
+ struct FilterInfo_t* p_next;
+} FilterInfo;
+
+struct AndroidLogFormat_t {
+ android_LogPriority global_pri;
+ FilterInfo* filters;
+ AndroidLogPrintFormat format;
+ bool colored_output;
+ bool usec_time_output;
+ bool nsec_time_output;
+ bool printable_output;
+ bool year_output;
+ bool zone_output;
+ bool epoch_output;
+ bool monotonic_output;
+ bool uid_output;
+ bool descriptive_output;
+};
+
+/*
+ * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
+ * during android_log_processBinaryLogBuffer(), so we break layering.
+ */
+static bool descriptive_output = false;
+
+/*
+ * gnome-terminal color tags
+ * See http://misc.flogisoft.com/bash/tip_colors_and_formatting
+ * for ideas on how to set the forground color of the text for xterm.
+ * The color manipulation character stream is defined as:
+ * ESC [ 3 8 ; 5 ; <color#> m
+ */
+#define ANDROID_COLOR_BLUE 75
+#define ANDROID_COLOR_DEFAULT 231
+#define ANDROID_COLOR_GREEN 40
+#define ANDROID_COLOR_ORANGE 166
+#define ANDROID_COLOR_RED 196
+#define ANDROID_COLOR_YELLOW 226
+
+static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
+ FilterInfo* p_ret;
+
+ p_ret = (FilterInfo*)calloc(1, sizeof(FilterInfo));
+ p_ret->mTag = strdup(tag);
+ p_ret->mPri = pri;
+
+ return p_ret;
+}
+
+/* balance to above, filterinfo_free left unimplemented */
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri(char c) {
+ android_LogPriority pri;
+
+ c = tolower(c);
+
+ if (c >= '0' && c <= '9') {
+ if (c >= ('0' + ANDROID_LOG_SILENT)) {
+ pri = ANDROID_LOG_VERBOSE;
+ } else {
+ pri = (android_LogPriority)(c - '0');
+ }
+ } else if (c == 'v') {
+ pri = ANDROID_LOG_VERBOSE;
+ } else if (c == 'd') {
+ pri = ANDROID_LOG_DEBUG;
+ } else if (c == 'i') {
+ pri = ANDROID_LOG_INFO;
+ } else if (c == 'w') {
+ pri = ANDROID_LOG_WARN;
+ } else if (c == 'e') {
+ pri = ANDROID_LOG_ERROR;
+ } else if (c == 'f') {
+ pri = ANDROID_LOG_FATAL;
+ } else if (c == 's') {
+ pri = ANDROID_LOG_SILENT;
+ } else if (c == '*') {
+ pri = ANDROID_LOG_DEFAULT;
+ } else {
+ pri = ANDROID_LOG_UNKNOWN;
+ }
+
+ return pri;
+}
+
+static char filterPriToChar(android_LogPriority pri) {
+ switch (pri) {
+ /* clang-format off */
+ case ANDROID_LOG_VERBOSE: return 'V';
+ case ANDROID_LOG_DEBUG: return 'D';
+ case ANDROID_LOG_INFO: return 'I';
+ case ANDROID_LOG_WARN: return 'W';
+ case ANDROID_LOG_ERROR: return 'E';
+ case ANDROID_LOG_FATAL: return 'F';
+ case ANDROID_LOG_SILENT: return 'S';
+
+ case ANDROID_LOG_DEFAULT:
+ case ANDROID_LOG_UNKNOWN:
+ default: return '?';
+ /* clang-format on */
+ }
+}
+
+static int colorFromPri(android_LogPriority pri) {
+ switch (pri) {
+ /* clang-format off */
+ case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
+ case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE;
+ case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN;
+ case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE;
+ case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED;
+ case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED;
+ case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT;
+
+ case ANDROID_LOG_DEFAULT:
+ case ANDROID_LOG_UNKNOWN:
+ default: return ANDROID_COLOR_DEFAULT;
+ /* clang-format on */
+ }
+}
+
+static android_LogPriority filterPriForTag(AndroidLogFormat* p_format, const char* tag) {
+ FilterInfo* p_curFilter;
+
+ for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next) {
+ if (0 == strcmp(tag, p_curFilter->mTag)) {
+ if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
+ return p_format->global_pri;
+ } else {
+ return p_curFilter->mPri;
+ }
+ }
+ }
+
+ return p_format->global_pri;
+}
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
+ android_LogPriority pri) {
+ return pri >= filterPriForTag(p_format, tag);
+}
+
+AndroidLogFormat* android_log_format_new() {
+ AndroidLogFormat* p_ret;
+
+ p_ret = static_cast<AndroidLogFormat*>(calloc(1, sizeof(AndroidLogFormat)));
+
+ p_ret->global_pri = ANDROID_LOG_VERBOSE;
+ p_ret->format = FORMAT_BRIEF;
+ p_ret->colored_output = false;
+ p_ret->usec_time_output = false;
+ p_ret->nsec_time_output = false;
+ p_ret->printable_output = false;
+ p_ret->year_output = false;
+ p_ret->zone_output = false;
+ p_ret->epoch_output = false;
+#ifdef __ANDROID__
+ p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
+#else
+ p_ret->monotonic_output = false;
+#endif
+ p_ret->uid_output = false;
+ p_ret->descriptive_output = false;
+ descriptive_output = false;
+
+ return p_ret;
+}
+
+static list_declare(convertHead);
+
+void android_log_format_free(AndroidLogFormat* p_format) {
+ FilterInfo *p_info, *p_info_old;
+
+ p_info = p_format->filters;
+
+ while (p_info != NULL) {
+ p_info_old = p_info;
+ p_info = p_info->p_next;
+
+ free(p_info_old);
+ }
+
+ free(p_format);
+
+ /* Free conversion resource, can always be reconstructed */
+ while (!list_empty(&convertHead)) {
+ struct listnode* node = list_head(&convertHead);
+ list_remove(node);
+ LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
+ free(node);
+ }
+}
+
+int android_log_setPrintFormat(AndroidLogFormat* p_format, AndroidLogPrintFormat format) {
+ switch (format) {
+ case FORMAT_MODIFIER_COLOR:
+ p_format->colored_output = true;
+ return 0;
+ case FORMAT_MODIFIER_TIME_USEC:
+ p_format->usec_time_output = true;
+ return 0;
+ case FORMAT_MODIFIER_TIME_NSEC:
+ p_format->nsec_time_output = true;
+ return 0;
+ case FORMAT_MODIFIER_PRINTABLE:
+ p_format->printable_output = true;
+ return 0;
+ case FORMAT_MODIFIER_YEAR:
+ p_format->year_output = true;
+ return 0;
+ case FORMAT_MODIFIER_ZONE:
+ p_format->zone_output = !p_format->zone_output;
+ return 0;
+ case FORMAT_MODIFIER_EPOCH:
+ p_format->epoch_output = true;
+ return 0;
+ case FORMAT_MODIFIER_MONOTONIC:
+ p_format->monotonic_output = true;
+ return 0;
+ case FORMAT_MODIFIER_UID:
+ p_format->uid_output = true;
+ return 0;
+ case FORMAT_MODIFIER_DESCRIPT:
+ p_format->descriptive_output = true;
+ descriptive_output = true;
+ return 0;
+ default:
+ break;
+ }
+ p_format->format = format;
+ return 1;
+}
+
+#ifndef __MINGW32__
+static const char tz[] = "TZ";
+static const char utc[] = "UTC";
+#endif
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {
+ static AndroidLogPrintFormat format;
+
+ /* clang-format off */
+ if (!strcmp(formatString, "brief")) format = FORMAT_BRIEF;
+ else if (!strcmp(formatString, "process")) format = FORMAT_PROCESS;
+ else if (!strcmp(formatString, "tag")) format = FORMAT_TAG;
+ else if (!strcmp(formatString, "thread")) format = FORMAT_THREAD;
+ else if (!strcmp(formatString, "raw")) format = FORMAT_RAW;
+ else if (!strcmp(formatString, "time")) format = FORMAT_TIME;
+ else if (!strcmp(formatString, "threadtime")) format = FORMAT_THREADTIME;
+ else if (!strcmp(formatString, "long")) format = FORMAT_LONG;
+ else if (!strcmp(formatString, "color")) format = FORMAT_MODIFIER_COLOR;
+ else if (!strcmp(formatString, "colour")) format = FORMAT_MODIFIER_COLOR;
+ else if (!strcmp(formatString, "usec")) format = FORMAT_MODIFIER_TIME_USEC;
+ else if (!strcmp(formatString, "nsec")) format = FORMAT_MODIFIER_TIME_NSEC;
+ else if (!strcmp(formatString, "printable")) format = FORMAT_MODIFIER_PRINTABLE;
+ else if (!strcmp(formatString, "year")) format = FORMAT_MODIFIER_YEAR;
+ else if (!strcmp(formatString, "zone")) format = FORMAT_MODIFIER_ZONE;
+ else if (!strcmp(formatString, "epoch")) format = FORMAT_MODIFIER_EPOCH;
+ else if (!strcmp(formatString, "monotonic")) format = FORMAT_MODIFIER_MONOTONIC;
+ else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
+ else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
+ /* clang-format on */
+
+#ifndef __MINGW32__
+ else {
+ extern char* tzname[2];
+ static const char gmt[] = "GMT";
+ char* cp = getenv(tz);
+ if (cp) {
+ cp = strdup(cp);
+ }
+ setenv(tz, formatString, 1);
+ /*
+ * Run tzset here to determine if the timezone is legitimate. If the
+ * zone is GMT, check if that is what was asked for, if not then
+ * did not match any on the system; report an error to caller.
+ */
+ tzset();
+ if (!tzname[0] ||
+ ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt)) /* error? */
+ && strcasecmp(formatString, utc) && strcasecmp(formatString, gmt))) { /* ok */
+ if (cp) {
+ setenv(tz, cp, 1);
+ } else {
+ unsetenv(tz);
+ }
+ tzset();
+ format = FORMAT_OFF;
+ } else {
+ format = FORMAT_MODIFIER_ZONE;
+ }
+ free(cp);
+ }
+#endif
+
+ return format;
+}
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule(AndroidLogFormat* p_format, const char* filterExpression) {
+ size_t tagNameLength;
+ android_LogPriority pri = ANDROID_LOG_DEFAULT;
+
+ tagNameLength = strcspn(filterExpression, ":");
+
+ if (tagNameLength == 0) {
+ goto error;
+ }
+
+ if (filterExpression[tagNameLength] == ':') {
+ pri = filterCharToPri(filterExpression[tagNameLength + 1]);
+
+ if (pri == ANDROID_LOG_UNKNOWN) {
+ goto error;
+ }
+ }
+
+ if (0 == strncmp("*", filterExpression, tagNameLength)) {
+ /*
+ * This filter expression refers to the global filter
+ * The default level for this is DEBUG if the priority
+ * is unspecified
+ */
+ if (pri == ANDROID_LOG_DEFAULT) {
+ pri = ANDROID_LOG_DEBUG;
+ }
+
+ p_format->global_pri = pri;
+ } else {
+ /*
+ * for filter expressions that don't refer to the global
+ * filter, the default is verbose if the priority is unspecified
+ */
+ if (pri == ANDROID_LOG_DEFAULT) {
+ pri = ANDROID_LOG_VERBOSE;
+ }
+
+ char* tagName;
+
+/*
+ * Presently HAVE_STRNDUP is never defined, so the second case is always taken
+ * Darwin doesn't have strndup, everything else does
+ */
+#ifdef HAVE_STRNDUP
+ tagName = strndup(filterExpression, tagNameLength);
+#else
+ /* a few extra bytes copied... */
+ tagName = strdup(filterExpression);
+ tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+ FilterInfo* p_fi = filterinfo_new(tagName, pri);
+ free(tagName);
+
+ p_fi->p_next = p_format->filters;
+ p_format->filters = p_fi;
+ }
+
+ return 0;
+error:
+ return -1;
+}
+
+#ifndef HAVE_STRSEP
+/* KISS replacement helper for below */
+static char* strsep(char** stringp, const char* delim) {
+ char* token;
+ char* ret = *stringp;
+
+ if (!ret || !*ret) {
+ return NULL;
+ }
+ token = strpbrk(ret, delim);
+ if (token) {
+ *token = '\0';
+ ++token;
+ } else {
+ token = ret + strlen(ret);
+ }
+ *stringp = token;
+ return ret;
+}
+#endif
+
+/**
+ * filterString: a comma/whitespace-separated set of filter expressions
+ *
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_addFilterString(AndroidLogFormat* p_format, const char* filterString) {
+ char* filterStringCopy = strdup(filterString);
+ char* p_cur = filterStringCopy;
+ char* p_ret;
+ int err;
+
+ /* Yes, I'm using strsep */
+ while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
+ /* ignore whitespace-only entries */
+ if (p_ret[0] != '\0') {
+ err = android_log_addFilterRule(p_format, p_ret);
+
+ if (err < 0) {
+ goto error;
+ }
+ }
+ }
+
+ free(filterStringCopy);
+ return 0;
+error:
+ free(filterStringCopy);
+ return -1;
+}
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry* buf, AndroidLogEntry* entry) {
+ entry->message = NULL;
+ entry->messageLen = 0;
+
+ entry->tv_sec = buf->sec;
+ entry->tv_nsec = buf->nsec;
+ entry->uid = -1;
+ entry->pid = buf->pid;
+ entry->tid = buf->tid;
+
+ /*
+ * format: <priority:1><tag:N>\0<message:N>\0
+ *
+ * tag str
+ * starts at buf + buf->hdr_size + 1
+ * msg
+ * starts at buf + buf->hdr_size + 1 + len(tag) + 1
+ *
+ * The message may have been truncated. When that happens, we must null-terminate the message
+ * ourselves.
+ */
+ if (buf->len < 3) {
+ /*
+ * An well-formed entry must consist of at least a priority
+ * and two null characters
+ */
+ fprintf(stderr, "+++ LOG: entry too small\n");
+ return -1;
+ }
+
+ int msgStart = -1;
+ int msgEnd = -1;
+
+ int i;
+ if (buf->hdr_size < sizeof(logger_entry)) {
+ fprintf(stderr, "+++ LOG: hdr_size must be at least as big as struct logger_entry\n");
+ return -1;
+ }
+ char* msg = reinterpret_cast<char*>(buf) + buf->hdr_size;
+ entry->uid = buf->uid;
+
+ for (i = 1; i < buf->len; i++) {
+ if (msg[i] == '\0') {
+ if (msgStart == -1) {
+ msgStart = i + 1;
+ } else {
+ msgEnd = i;
+ break;
+ }
+ }
+ }
+
+ if (msgStart == -1) {
+ /* +++ LOG: malformed log message, DYB */
+ for (i = 1; i < buf->len; i++) {
+ /* odd characters in tag? */
+ if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+ msg[i] = '\0';
+ msgStart = i + 1;
+ break;
+ }
+ }
+ if (msgStart == -1) {
+ msgStart = buf->len - 1; /* All tag, no message, print truncates */
+ }
+ }
+ if (msgEnd == -1) {
+ /* incoming message not null-terminated; force it */
+ msgEnd = buf->len - 1; /* may result in msgEnd < msgStart */
+ msg[msgEnd] = '\0';
+ }
+
+ entry->priority = static_cast<android_LogPriority>(msg[0]);
+ entry->tag = msg + 1;
+ entry->tagLen = msgStart - 1;
+ entry->message = msg + msgStart;
+ entry->messageLen = (msgEnd < msgStart) ? 0 : (msgEnd - msgStart);
+
+ return 0;
+}
+
+static bool findChar(const char** cp, size_t* len, int c) {
+ while ((*len) && isspace(*(*cp))) {
+ ++(*cp);
+ --(*len);
+ }
+ if (c == INT_MAX) return *len;
+ if ((*len) && (*(*cp) == c)) {
+ ++(*cp);
+ --(*len);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Recursively convert binary log data to printable form.
+ *
+ * This needs to be recursive because you can have lists of lists.
+ *
+ * If we run out of room, we stop processing immediately. It's important
+ * for us to check for space on every output element to avoid producing
+ * garbled output.
+ *
+ * Returns 0 on success, 1 on buffer full, -1 on failure.
+ */
+enum objectType {
+ TYPE_OBJECTS = '1',
+ TYPE_BYTES = '2',
+ TYPE_MILLISECONDS = '3',
+ TYPE_ALLOCATIONS = '4',
+ TYPE_ID = '5',
+ TYPE_PERCENT = '6',
+ TYPE_MONOTONIC = 's'
+};
+
+static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen,
+ char** pOutBuf, size_t* pOutBufLen, const char** fmtStr,
+ size_t* fmtLen) {
+ const unsigned char* eventData = *pEventData;
+ size_t eventDataLen = *pEventDataLen;
+ char* outBuf = *pOutBuf;
+ char* outBufSave = outBuf;
+ size_t outBufLen = *pOutBufLen;
+ size_t outBufLenSave = outBufLen;
+ unsigned char type;
+ size_t outCount = 0;
+ int result = 0;
+ const char* cp;
+ size_t len;
+ int64_t lval;
+
+ if (eventDataLen < 1) return -1;
+
+ type = *eventData;
+
+ cp = NULL;
+ len = 0;
+ if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
+ cp = *fmtStr;
+ len = *fmtLen;
+ }
+ /*
+ * event.logtag format specification:
+ *
+ * Optionally, after the tag names can be put a description for the value(s)
+ * of the tag. Description are in the format
+ * (<name>|data type[|data unit])
+ * Multiple values are separated by commas.
+ *
+ * The data type is a number from the following values:
+ * 1: int
+ * 2: long
+ * 3: string
+ * 4: list
+ * 5: float
+ *
+ * The data unit is a number taken from the following list:
+ * 1: Number of objects
+ * 2: Number of bytes
+ * 3: Number of milliseconds
+ * 4: Number of allocations
+ * 5: Id
+ * 6: Percent
+ * s: Number of seconds (monotonic time)
+ * Default value for data of type int/long is 2 (bytes).
+ */
+ if (!cp || !findChar(&cp, &len, '(')) {
+ len = 0;
+ } else {
+ char* outBufLastSpace = NULL;
+
+ findChar(&cp, &len, INT_MAX);
+ while (len && *cp && (*cp != '|') && (*cp != ')')) {
+ if (outBufLen <= 0) {
+ /* halt output */
+ goto no_room;
+ }
+ outBufLastSpace = isspace(*cp) ? outBuf : NULL;
+ *outBuf = *cp;
+ ++outBuf;
+ ++cp;
+ --outBufLen;
+ --len;
+ }
+ if (outBufLastSpace) {
+ outBufLen += outBuf - outBufLastSpace;
+ outBuf = outBufLastSpace;
+ }
+ if (outBufLen <= 0) {
+ /* halt output */
+ goto no_room;
+ }
+ if (outBufSave != outBuf) {
+ *outBuf = '=';
+ ++outBuf;
+ --outBufLen;
+ }
+
+ if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+ static const unsigned char typeTable[] = {EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING,
+ EVENT_TYPE_LIST, EVENT_TYPE_FLOAT};
+
+ if ((*cp >= '1') && (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+ (type != typeTable[(size_t)(*cp - '1')]))
+ len = 0;
+
+ if (len) {
+ ++cp;
+ --len;
+ } else {
+ /* reset the format */
+ outBuf = outBufSave;
+ outBufLen = outBufLenSave;
+ }
+ }
+ }
+ outCount = 0;
+ lval = 0;
+ switch (type) {
+ case EVENT_TYPE_INT:
+ /* 32-bit signed int */
+ {
+ if (eventDataLen < sizeof(android_event_int_t)) return -1;
+ auto* event_int = reinterpret_cast<const android_event_int_t*>(eventData);
+ lval = event_int->data;
+ eventData += sizeof(android_event_int_t);
+ eventDataLen -= sizeof(android_event_int_t);
+ }
+ goto pr_lval;
+ case EVENT_TYPE_LONG:
+ /* 64-bit signed long */
+ if (eventDataLen < sizeof(android_event_long_t)) {
+ return -1;
+ }
+ {
+ auto* event_long = reinterpret_cast<const android_event_long_t*>(eventData);
+ lval = event_long->data;
+ }
+ eventData += sizeof(android_event_long_t);
+ eventDataLen -= sizeof(android_event_long_t);
+ pr_lval:
+ outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ break;
+ case EVENT_TYPE_FLOAT:
+ /* float */
+ {
+ if (eventDataLen < sizeof(android_event_float_t)) return -1;
+ auto* event_float = reinterpret_cast<const android_event_float_t*>(eventData);
+ float fval = event_float->data;
+ eventData += sizeof(android_event_int_t);
+ eventDataLen -= sizeof(android_event_int_t);
+
+ outCount = snprintf(outBuf, outBufLen, "%f", fval);
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ break;
+ case EVENT_TYPE_STRING:
+ /* UTF-8 chars, not NULL-terminated */
+ {
+ if (eventDataLen < sizeof(android_event_string_t)) return -1;
+ auto* event_string = reinterpret_cast<const android_event_string_t*>(eventData);
+ unsigned int strLen = event_string->length;
+ eventData += sizeof(android_event_string_t);
+ eventDataLen -= sizeof(android_event_string_t);
+
+ if (eventDataLen < strLen) {
+ result = -1; /* mark truncated */
+ strLen = eventDataLen;
+ }
+
+ if (cp && (strLen == 0)) {
+ /* reset the format if no content */
+ outBuf = outBufSave;
+ outBufLen = outBufLenSave;
+ }
+ if (strLen < outBufLen) {
+ memcpy(outBuf, eventData, strLen);
+ outBuf += strLen;
+ outBufLen -= strLen;
+ } else {
+ if (outBufLen > 0) {
+ /* copy what we can */
+ memcpy(outBuf, eventData, outBufLen);
+ outBuf += outBufLen;
+ outBufLen -= outBufLen;
+ }
+ if (!result) result = 1; /* if not truncated, return no room */
+ }
+ eventData += strLen;
+ eventDataLen -= strLen;
+ if (result != 0) goto bail;
+ break;
+ }
+ case EVENT_TYPE_LIST:
+ /* N items, all different types */
+ {
+ if (eventDataLen < sizeof(android_event_list_t)) return -1;
+ auto* event_list = reinterpret_cast<const android_event_list_t*>(eventData);
+
+ int8_t count = event_list->element_count;
+ eventData += sizeof(android_event_list_t);
+ eventDataLen -= sizeof(android_event_list_t);
+
+ if (outBufLen <= 0) goto no_room;
+
+ *outBuf++ = '[';
+ outBufLen--;
+
+ for (int i = 0; i < count; i++) {
+ result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
+ fmtStr, fmtLen);
+ if (result != 0) goto bail;
+
+ if (i < (count - 1)) {
+ if (outBufLen <= 0) goto no_room;
+ *outBuf++ = ',';
+ outBufLen--;
+ }
+ }
+
+ if (outBufLen <= 0) goto no_room;
+
+ *outBuf++ = ']';
+ outBufLen--;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown binary event type %d\n", type);
+ return -1;
+ }
+ if (cp && len) {
+ if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+ switch (*cp) {
+ case TYPE_OBJECTS:
+ outCount = 0;
+ /* outCount = snprintf(outBuf, outBufLen, " objects"); */
+ break;
+ case TYPE_BYTES:
+ if ((lval != 0) && ((lval % 1024) == 0)) {
+ /* repaint with multiplier */
+ static const char suffixTable[] = {'K', 'M', 'G', 'T'};
+ size_t idx = 0;
+ outBuf -= outCount;
+ outBufLen += outCount;
+ do {
+ lval /= 1024;
+ if ((lval % 1024) != 0) break;
+ } while (++idx < ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
+ outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval, suffixTable[idx]);
+ } else {
+ outCount = snprintf(outBuf, outBufLen, "B");
+ }
+ break;
+ case TYPE_MILLISECONDS:
+ if (((lval <= -1000) || (1000 <= lval)) && (outBufLen || (outBuf[-1] == '0'))) {
+ /* repaint as (fractional) seconds, possibly saving space */
+ if (outBufLen) outBuf[0] = outBuf[-1];
+ outBuf[-1] = outBuf[-2];
+ outBuf[-2] = outBuf[-3];
+ outBuf[-3] = '.';
+ while ((outBufLen == 0) || (*outBuf == '0')) {
+ --outBuf;
+ ++outBufLen;
+ }
+ if (*outBuf != '.') {
+ ++outBuf;
+ --outBufLen;
+ }
+ outCount = snprintf(outBuf, outBufLen, "s");
+ } else {
+ outCount = snprintf(outBuf, outBufLen, "ms");
+ }
+ break;
+ case TYPE_MONOTONIC: {
+ static const uint64_t minute = 60;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ /* Repaint as unsigned seconds, minutes, hours ... */
+ outBuf -= outCount;
+ outBufLen += outCount;
+ uint64_t val = lval;
+ if (val >= day) {
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 "d ", val / day);
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":", (val / hour) % (day / hour));
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ }
+ outCount =
+ snprintf(outBuf, outBufLen, (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ if (outCount >= outBufLen) break;
+ outBuf += outCount;
+ outBufLen -= outCount;
+ }
+ outCount = snprintf(outBuf, outBufLen, (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+ val % minute);
+ } break;
+ case TYPE_ALLOCATIONS:
+ outCount = 0;
+ /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
+ break;
+ case TYPE_ID:
+ outCount = 0;
+ break;
+ case TYPE_PERCENT:
+ outCount = snprintf(outBuf, outBufLen, "%%");
+ break;
+ default: /* ? */
+ outCount = 0;
+ break;
+ }
+ ++cp;
+ --len;
+ if (outCount < outBufLen) {
+ outBuf += outCount;
+ outBufLen -= outCount;
+ } else if (outCount) {
+ /* halt output */
+ goto no_room;
+ }
+ }
+ if (!findChar(&cp, &len, ')')) len = 0;
+ if (!findChar(&cp, &len, ',')) len = 0;
+ }
+
+bail:
+ *pEventData = eventData;
+ *pEventDataLen = eventDataLen;
+ *pOutBuf = outBuf;
+ *pOutBufLen = outBufLen;
+ if (cp) {
+ *fmtStr = cp;
+ *fmtLen = len;
+ }
+ return result;
+
+no_room:
+ result = 1;
+ goto bail;
+}
+
+/**
+ * Convert a binary log entry to ASCII form.
+ *
+ * For convenience we mimic the processLogBuffer API. There is no
+ * pre-defined output length for the binary data, since we're free to format
+ * it however we choose, which means we can't really use a fixed-size buffer
+ * here.
+ */
+int android_log_processBinaryLogBuffer(
+ struct logger_entry* buf, AndroidLogEntry* entry,
+ [[maybe_unused]] const EventTagMap* map, /* only on !__ANDROID__ */
+ char* messageBuf, int messageBufLen) {
+ size_t inCount;
+ uint32_t tagIndex;
+ const unsigned char* eventData;
+
+ entry->message = NULL;
+ entry->messageLen = 0;
+
+ entry->tv_sec = buf->sec;
+ entry->tv_nsec = buf->nsec;
+ entry->priority = ANDROID_LOG_INFO;
+ entry->uid = -1;
+ entry->pid = buf->pid;
+ entry->tid = buf->tid;
+
+ if (buf->hdr_size < sizeof(logger_entry)) {
+ fprintf(stderr, "+++ LOG: hdr_size must be at least as big as struct logger_entry\n");
+ return -1;
+ }
+ eventData = reinterpret_cast<unsigned char*>(buf) + buf->hdr_size;
+ if (buf->lid == LOG_ID_SECURITY) {
+ entry->priority = ANDROID_LOG_WARN;
+ }
+ entry->uid = buf->uid;
+ inCount = buf->len;
+ if (inCount < sizeof(android_event_header_t)) return -1;
+ auto* event_header = reinterpret_cast<const android_event_header_t*>(eventData);
+ tagIndex = event_header->tag;
+ eventData += sizeof(android_event_header_t);
+ inCount -= sizeof(android_event_header_t);
+
+ entry->tagLen = 0;
+ entry->tag = NULL;
+#ifdef __ANDROID__
+ if (map != NULL) {
+ entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
+ }
+#endif
+
+ /*
+ * If we don't have a map, or didn't find the tag number in the map,
+ * stuff a generated tag value into the start of the output buffer and
+ * shift the buffer pointers down.
+ */
+ if (entry->tag == NULL) {
+ size_t tagLen;
+
+ tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
+ if (tagLen >= (size_t)messageBufLen) {
+ tagLen = messageBufLen - 1;
+ }
+ entry->tag = messageBuf;
+ entry->tagLen = tagLen;
+ messageBuf += tagLen + 1;
+ messageBufLen -= tagLen + 1;
+ }
+
+ /*
+ * Format the event log data into the buffer.
+ */
+ const char* fmtStr = NULL;
+ size_t fmtLen = 0;
+#ifdef __ANDROID__
+ if (descriptive_output && map) {
+ fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
+ }
+#endif
+
+ char* outBuf = messageBuf;
+ size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
+ int result = 0;
+
+ if ((inCount > 0) || fmtLen) {
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, &fmtStr,
+ &fmtLen);
+ }
+ if ((result == 1) && fmtStr) {
+ /* We overflowed :-(, let's repaint the line w/o format dressings */
+ eventData = reinterpret_cast<unsigned char*>(buf) + buf->hdr_size;
+ eventData += 4;
+ outBuf = messageBuf;
+ outRemaining = messageBufLen - 1;
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, NULL, NULL);
+ }
+ if (result < 0) {
+ fprintf(stderr, "Binary log entry conversion failed\n");
+ }
+ if (result) {
+ if (!outRemaining) {
+ /* make space to leave an indicator */
+ --outBuf;
+ ++outRemaining;
+ }
+ *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
+ outRemaining--;
+ /* pretend we ate all the data to prevent log stutter */
+ inCount = 0;
+ if (result > 0) result = 0;
+ }
+
+ /* eat the silly terminating '\n' */
+ if (inCount == 1 && *eventData == '\n') {
+ eventData++;
+ inCount--;
+ }
+
+ if (inCount != 0) {
+ fprintf(stderr, "Warning: leftover binary log data (%zu bytes)\n", inCount);
+ }
+
+ /*
+ * Terminate the buffer. The NUL byte does not count as part of
+ * entry->messageLen.
+ */
+ *outBuf = '\0';
+ entry->messageLen = outBuf - messageBuf;
+ assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
+
+ entry->message = messageBuf;
+
+ return result;
+}
+
+/*
+ * Convert to printable from message to p buffer, return string length. If p is
+ * NULL, do not copy, but still return the expected string length.
+ */
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+ char* begin = p;
+ bool print = p != NULL;
+ mbstate_t mb_state = {};
+
+ while (messageLen) {
+ char buf[6];
+ ssize_t len = sizeof(buf) - 1;
+ if ((size_t)len > messageLen) {
+ len = messageLen;
+ }
+ len = mbrtowc(nullptr, message, len, &mb_state);
+
+ if (len < 0) {
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+ len = 1;
+ } else {
+ buf[0] = '\0';
+ if (len == 1) {
+ if (*message == '\a') {
+ strcpy(buf, "\\a");
+ } else if (*message == '\b') {
+ strcpy(buf, "\\b");
+ } else if (*message == '\t') {
+ strcpy(buf, "\t"); /* Do not escape tabs */
+ } else if (*message == '\v') {
+ strcpy(buf, "\\v");
+ } else if (*message == '\f') {
+ strcpy(buf, "\\f");
+ } else if (*message == '\r') {
+ strcpy(buf, "\\r");
+ } else if (*message == '\\') {
+ strcpy(buf, "\\\\");
+ } else if ((*message < ' ') || (*message & 0x80)) {
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+ }
+ }
+ if (!buf[0]) {
+ strncpy(buf, message, len);
+ buf[len] = '\0';
+ }
+ }
+ if (print) {
+ strcpy(p, buf);
+ }
+ p += strlen(buf);
+ message += len;
+ messageLen -= len;
+ }
+ return p - begin;
+}
+
+#ifdef __ANDROID__
+static char* readSeconds(char* e, struct timespec* t) {
+ unsigned long multiplier;
+ char* p;
+ t->tv_sec = strtoul(e, &p, 10);
+ if (*p != '.') {
+ return NULL;
+ }
+ t->tv_nsec = 0;
+ multiplier = NS_PER_SEC;
+ while (isdigit(*++p) && (multiplier /= 10)) {
+ t->tv_nsec += (*p - '0') * multiplier;
+ }
+ return p;
+}
+
+static struct timespec* sumTimespec(struct timespec* left, struct timespec* right) {
+ left->tv_nsec += right->tv_nsec;
+ left->tv_sec += right->tv_sec;
+ if (left->tv_nsec >= (long)NS_PER_SEC) {
+ left->tv_nsec -= NS_PER_SEC;
+ left->tv_sec += 1;
+ }
+ return left;
+}
+
+static struct timespec* subTimespec(struct timespec* result, struct timespec* left,
+ struct timespec* right) {
+ result->tv_nsec = left->tv_nsec - right->tv_nsec;
+ result->tv_sec = left->tv_sec - right->tv_sec;
+ if (result->tv_nsec < 0) {
+ result->tv_nsec += NS_PER_SEC;
+ result->tv_sec -= 1;
+ }
+ return result;
+}
+
+static long long nsecTimespec(struct timespec* now) {
+ return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+static void convertMonotonic(struct timespec* result, const AndroidLogEntry* entry) {
+ struct listnode* node;
+ struct conversionList {
+ struct listnode node; /* first */
+ struct timespec time;
+ struct timespec convert;
+ } * list, *next;
+ struct timespec time, convert;
+
+ /* If we do not have a conversion list, build one up */
+ if (list_empty(&convertHead)) {
+ bool suspended_pending = false;
+ struct timespec suspended_monotonic = {0, 0};
+ struct timespec suspended_diff = {0, 0};
+
+ /*
+ * Read dmesg for _some_ synchronization markers and insert
+ * Anything in the Android Logger before the dmesg logging span will
+ * be highly suspect regarding the monotonic time calculations.
+ */
+ FILE* p = popen("/system/bin/dmesg", "re");
+ if (p) {
+ char* line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, p) > 0) {
+ static const char suspend[] = "PM: suspend entry ";
+ static const char resume[] = "PM: suspend exit ";
+ static const char healthd[] = "healthd";
+ static const char battery[] = ": battery ";
+ static const char suspended[] = "Suspended for ";
+ struct timespec monotonic;
+ struct tm tm;
+ char *cp, *e = line;
+ bool add_entry = true;
+
+ if (*e == '<') {
+ while (*e && (*e != '>')) {
+ ++e;
+ }
+ if (*e != '>') {
+ continue;
+ }
+ }
+ if (*e != '[') {
+ continue;
+ }
+ while (*++e == ' ') {
+ ;
+ }
+ e = readSeconds(e, &monotonic);
+ if (!e || (*e != ']')) {
+ continue;
+ }
+
+ if ((e = strstr(e, suspend))) {
+ e += sizeof(suspend) - 1;
+ } else if ((e = strstr(line, resume))) {
+ e += sizeof(resume) - 1;
+ } else if (((e = strstr(line, healthd))) &&
+ ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+ /* NB: healthd is roughly 150us late, worth the price to
+ * deal with ntp-induced or hardware clock drift. */
+ e += sizeof(battery) - 1;
+ } else if ((e = strstr(line, suspended))) {
+ e += sizeof(suspended) - 1;
+ e = readSeconds(e, &time);
+ if (!e) {
+ continue;
+ }
+ add_entry = false;
+ suspended_pending = true;
+ suspended_monotonic = monotonic;
+ suspended_diff = time;
+ } else {
+ continue;
+ }
+ if (add_entry) {
+ /* look for "????-??-?? ??:??:??.????????? UTC" */
+ cp = strstr(e, " UTC");
+ if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+ continue;
+ }
+ e = cp - 29;
+ cp = readSeconds(cp - 10, &time);
+ if (!cp) {
+ continue;
+ }
+ cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+ if (!cp) {
+ continue;
+ }
+ cp = getenv(tz);
+ if (cp) {
+ cp = strdup(cp);
+ }
+ setenv(tz, utc, 1);
+ time.tv_sec = mktime(&tm);
+ if (cp) {
+ setenv(tz, cp, 1);
+ free(cp);
+ } else {
+ unsetenv(tz);
+ }
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+ list_init(&list->node);
+ list->time = time;
+ subTimespec(&list->convert, &time, &monotonic);
+ list_add_tail(&convertHead, &list->node);
+ }
+ if (suspended_pending && !list_empty(&convertHead)) {
+ list = node_to_item(list_tail(&convertHead), struct conversionList, node);
+ if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
+ &suspended_monotonic)
+ ->tv_sec > 0) {
+ /* resume, what is convert factor before? */
+ subTimespec(&convert, &list->convert, &suspended_diff);
+ } else {
+ /* suspend */
+ convert = list->convert;
+ }
+ time = suspended_monotonic;
+ sumTimespec(&time, &convert);
+ /* breakpoint just before sleep */
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+ list_init(&list->node);
+ list->time = time;
+ list->convert = convert;
+ list_add_tail(&convertHead, &list->node);
+ /* breakpoint just after sleep */
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+ list_init(&list->node);
+ list->time = time;
+ sumTimespec(&list->time, &suspended_diff);
+ list->convert = convert;
+ sumTimespec(&list->convert, &suspended_diff);
+ list_add_tail(&convertHead, &list->node);
+ suspended_pending = false;
+ }
+ }
+ pclose(p);
+ }
+ /* last entry is our current time conversion */
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+ list_init(&list->node);
+ clock_gettime(CLOCK_REALTIME, &list->time);
+ clock_gettime(CLOCK_MONOTONIC, &convert);
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+ subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+ /* Calculate conversion factor */
+ subTimespec(&list->convert, &list->time, &time);
+ list_add_tail(&convertHead, &list->node);
+ if (suspended_pending) {
+ /* manufacture a suspend @ point before */
+ subTimespec(&convert, &list->convert, &suspended_diff);
+ time = suspended_monotonic;
+ sumTimespec(&time, &convert);
+ /* breakpoint just after sleep */
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+ list_init(&list->node);
+ list->time = time;
+ sumTimespec(&list->time, &suspended_diff);
+ list->convert = convert;
+ sumTimespec(&list->convert, &suspended_diff);
+ list_add_head(&convertHead, &list->node);
+ /* breakpoint just before sleep */
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
+ list_init(&list->node);
+ list->time = time;
+ list->convert = convert;
+ list_add_head(&convertHead, &list->node);
+ }
+ }
+
+ /* Find the breakpoint in the conversion list */
+ list = node_to_item(list_head(&convertHead), struct conversionList, node);
+ next = NULL;
+ list_for_each(node, &convertHead) {
+ next = node_to_item(node, struct conversionList, node);
+ if (entry->tv_sec < next->time.tv_sec) {
+ break;
+ } else if (entry->tv_sec == next->time.tv_sec) {
+ if (entry->tv_nsec < next->time.tv_nsec) {
+ break;
+ }
+ }
+ list = next;
+ }
+
+ /* blend time from one breakpoint to the next */
+ convert = list->convert;
+ if (next) {
+ unsigned long long total, run;
+
+ total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+ time.tv_sec = entry->tv_sec;
+ time.tv_nsec = entry->tv_nsec;
+ run = nsecTimespec(subTimespec(&time, &time, &list->time));
+ if (run < total) {
+ long long crun;
+
+ float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+ f *= run;
+ f /= total;
+ crun = f;
+ convert.tv_sec += crun / (long long)NS_PER_SEC;
+ if (crun < 0) {
+ convert.tv_nsec -= (-crun) % NS_PER_SEC;
+ if (convert.tv_nsec < 0) {
+ convert.tv_nsec += NS_PER_SEC;
+ convert.tv_sec -= 1;
+ }
+ } else {
+ convert.tv_nsec += crun % NS_PER_SEC;
+ if (convert.tv_nsec >= (long)NS_PER_SEC) {
+ convert.tv_nsec -= NS_PER_SEC;
+ convert.tv_sec += 1;
+ }
+ }
+ }
+ }
+
+ /* Apply the correction factor */
+ result->tv_sec = entry->tv_sec;
+ result->tv_nsec = entry->tv_nsec;
+ subTimespec(result, result, &convert);
+}
+#endif
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
+ size_t defaultBufferSize, const AndroidLogEntry* entry,
+ size_t* p_outLength) {
+#if !defined(_WIN32)
+ struct tm tmBuf;
+#endif
+ struct tm* ptm;
+ /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
+ char timeBuf[64];
+ char prefixBuf[128], suffixBuf[128];
+ char priChar;
+ int prefixSuffixIsHeaderFooter = 0;
+ char* ret;
+ time_t now;
+ unsigned long nsec;
+
+ priChar = filterPriToChar(entry->priority);
+ size_t prefixLen = 0, suffixLen = 0;
+ size_t len;
+
+ /*
+ * Get the current date/time in pretty form
+ *
+ * It's often useful when examining a log with "less" to jump to
+ * a specific point in the file by searching for the date/time stamp.
+ * For this reason it's very annoying to have regexp meta characters
+ * in the time stamp. Don't use forward slashes, parenthesis,
+ * brackets, asterisks, or other special chars here.
+ *
+ * The caller may have affected the timezone environment, this is
+ * expected to be sensitive to that.
+ */
+ now = entry->tv_sec;
+ nsec = entry->tv_nsec;
+#if __ANDROID__
+ if (p_format->monotonic_output) {
+ /* prevent convertMonotonic from being called if logd is monotonic */
+ if (android_log_clockid() != CLOCK_MONOTONIC) {
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
+ }
+ }
+#endif
+ if (now < 0) {
+ nsec = NS_PER_SEC - nsec;
+ }
+ if (p_format->epoch_output || p_format->monotonic_output) {
+ ptm = NULL;
+ snprintf(timeBuf, sizeof(timeBuf), p_format->monotonic_output ? "%6lld" : "%19lld",
+ (long long)now);
+ } else {
+#if !defined(_WIN32)
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+ strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
+ }
+ len = strlen(timeBuf);
+ if (p_format->nsec_time_output) {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
+ } else if (p_format->usec_time_output) {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", nsec / US_PER_NSEC);
+ } else {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", nsec / MS_PER_NSEC);
+ }
+ if (p_format->zone_output && ptm) {
+ strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
+ }
+
+ /*
+ * Construct a buffer containing the log header and log message.
+ */
+ if (p_format->colored_output) {
+ prefixLen =
+ snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
+ prefixLen = MIN(prefixLen, sizeof(prefixBuf));
+
+ const char suffixContents[] = "\x1B[0m";
+ strcpy(suffixBuf, suffixContents);
+ suffixLen = strlen(suffixContents);
+ }
+
+ char uid[16];
+ uid[0] = '\0';
+ if (p_format->uid_output) {
+ if (entry->uid >= 0) {
+/*
+ * This code is Android specific, bionic guarantees that
+ * calls to non-reentrant getpwuid() are thread safe.
+ */
+#ifdef __ANDROID__
+ struct passwd* pwd = getpwuid(entry->uid);
+ if (pwd && (strlen(pwd->pw_name) <= 5)) {
+ snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
+ } else
+#endif
+ {
+ /* Not worth parsing package list, names all longer than 5 */
+ snprintf(uid, sizeof(uid), "%5d:", entry->uid);
+ }
+ } else {
+ snprintf(uid, sizeof(uid), " ");
+ }
+ }
+
+ switch (p_format->format) {
+ case FORMAT_TAG:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8.*s: ", priChar,
+ (int)entry->tagLen, entry->tag);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_PROCESS:
+ len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, " (%.*s)\n",
+ (int)entry->tagLen, entry->tag);
+ suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d) ", priChar,
+ uid, entry->pid);
+ break;
+ case FORMAT_THREAD:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d:%5d) ",
+ priChar, uid, entry->pid, entry->tid);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_RAW:
+ prefixBuf[prefixLen] = 0;
+ len = 0;
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_TIME:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar, (int)entry->tagLen, entry->tag, uid,
+ entry->pid);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_THREADTIME:
+ ret = strchr(uid, ':');
+ if (ret) {
+ *ret = ' ';
+ }
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid, entry->tid, priChar,
+ (int)entry->tagLen, entry->tag);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ case FORMAT_LONG:
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid, entry->tid, priChar,
+ (int)entry->tagLen, entry->tag);
+ strcpy(suffixBuf + suffixLen, "\n\n");
+ suffixLen += 2;
+ prefixSuffixIsHeaderFooter = 1;
+ break;
+ case FORMAT_BRIEF:
+ default:
+ len =
+ snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag, uid, entry->pid);
+ strcpy(suffixBuf + suffixLen, "\n");
+ ++suffixLen;
+ break;
+ }
+
+ /* snprintf has a weird return value. It returns what would have been
+ * written given a large enough buffer. In the case that the prefix is
+ * longer then our buffer(128), it messes up the calculations below
+ * possibly causing heap corruption. To avoid this we double check and
+ * set the length at the maximum (size minus null byte)
+ */
+ prefixLen += len;
+ if (prefixLen >= sizeof(prefixBuf)) {
+ prefixLen = sizeof(prefixBuf) - 1;
+ prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+ }
+ if (suffixLen >= sizeof(suffixBuf)) {
+ suffixLen = sizeof(suffixBuf) - 1;
+ suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+ suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+ }
+
+ /* the following code is tragically unreadable */
+
+ size_t numLines;
+ char* p;
+ size_t bufferSize;
+ const char* pm;
+
+ if (prefixSuffixIsHeaderFooter) {
+ /* we're just wrapping message with a header/footer */
+ numLines = 1;
+ } else {
+ pm = entry->message;
+ numLines = 0;
+
+ /*
+ * The line-end finding here must match the line-end finding
+ * in for ( ... numLines...) loop below
+ */
+ while (pm < (entry->message + entry->messageLen)) {
+ if (*pm++ == '\n') numLines++;
+ }
+ /* plus one line for anything not newline-terminated at the end */
+ if (pm > entry->message && *(pm - 1) != '\n') numLines++;
+ }
+
+ /*
+ * this is an upper bound--newlines in message may be counted
+ * extraneously
+ */
+ bufferSize = (numLines * (prefixLen + suffixLen)) + 1;
+ if (p_format->printable_output) {
+ /* Calculate extra length to convert non-printable to printable */
+ bufferSize += convertPrintable(NULL, entry->message, entry->messageLen);
+ } else {
+ bufferSize += entry->messageLen;
+ }
+
+ if (defaultBufferSize >= bufferSize) {
+ ret = defaultBuffer;
+ } else {
+ ret = (char*)malloc(bufferSize);
+
+ if (ret == NULL) {
+ return ret;
+ }
+ }
+
+ ret[0] = '\0'; /* to start strcat off */
+
+ p = ret;
+ pm = entry->message;
+
+ if (prefixSuffixIsHeaderFooter) {
+ strcat(p, prefixBuf);
+ p += prefixLen;
+ if (p_format->printable_output) {
+ p += convertPrintable(p, entry->message, entry->messageLen);
+ } else {
+ strncat(p, entry->message, entry->messageLen);
+ p += entry->messageLen;
+ }
+ strcat(p, suffixBuf);
+ p += suffixLen;
+ } else {
+ do {
+ const char* lineStart;
+ size_t lineLen;
+ lineStart = pm;
+
+ /* Find the next end-of-line in message */
+ while (pm < (entry->message + entry->messageLen) && *pm != '\n') pm++;
+ lineLen = pm - lineStart;
+
+ strcat(p, prefixBuf);
+ p += prefixLen;
+ if (p_format->printable_output) {
+ p += convertPrintable(p, lineStart, lineLen);
+ } else {
+ strncat(p, lineStart, lineLen);
+ p += lineLen;
+ }
+ strcat(p, suffixBuf);
+ p += suffixLen;
+
+ if (*pm == '\n') pm++;
+ } while (pm < (entry->message + entry->messageLen));
+ }
+
+ if (p_outLength != NULL) {
+ *p_outLength = p - ret;
+ }
+
+ return ret;
+}
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Returns count bytes written
+ */
+
+int android_log_printLogLine(AndroidLogFormat* p_format, int fd, const AndroidLogEntry* entry) {
+ int ret;
+ char defaultBuffer[512];
+ char* outBuffer = NULL;
+ size_t totalLen;
+
+ outBuffer =
+ android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
+
+ if (!outBuffer) return -1;
+
+ do {
+ ret = write(fd, outBuffer, totalLen);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+ ret = 0;
+ goto done;
+ }
+
+ if (((size_t)ret) < totalLen) {
+ fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen);
+ goto done;
+ }
+
+done:
+ if (outBuffer != defaultBuffer) {
+ free(outBuffer);
+ }
+
+ return ret;
+}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
new file mode 100644
index 000000000..3bdc30f09
--- /dev/null
+++ b/liblog/pmsg_reader.cpp
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pmsg_reader.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/list.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg) {
+ ssize_t ret;
+ off_t current, next;
+ struct __attribute__((__packed__)) {
+ android_pmsg_log_header_t p;
+ android_log_header_t l;
+ uint8_t prio;
+ } buf;
+ static uint8_t preread_count;
+
+ memset(log_msg, 0, sizeof(*log_msg));
+
+ if (atomic_load(&logger_list->fd) <= 0) {
+ int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+
+ if (fd < 0) {
+ return -errno;
+ }
+ if (fd == 0) { /* Argggg */
+ fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
+ close(0);
+ if (fd < 0) {
+ return -errno;
+ }
+ }
+ i = atomic_exchange(&logger_list->fd, fd);
+ if ((i > 0) && (i != fd)) {
+ close(i);
+ }
+ preread_count = 0;
+ }
+
+ while (1) {
+ int fd;
+
+ if (preread_count < sizeof(buf)) {
+ fd = atomic_load(&logger_list->fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
+ if (ret < 0) {
+ return -errno;
+ }
+ preread_count += ret;
+ }
+ if (preread_count != sizeof(buf)) {
+ return preread_count ? -EIO : -EAGAIN;
+ }
+ if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
+ (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) || (buf.l.id >= LOG_ID_MAX) ||
+ (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+ ((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
+ ((buf.prio == ANDROID_LOG_UNKNOWN) || (buf.prio == ANDROID_LOG_DEFAULT) ||
+ (buf.prio >= ANDROID_LOG_SILENT)))) {
+ do {
+ memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
+ } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
+ continue;
+ }
+ preread_count = 0;
+
+ if ((logger_list->log_mask & (1 << buf.l.id)) &&
+ ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
+ ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
+ ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
+ (logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
+ (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
+ char* msg = reinterpret_cast<char*>(&log_msg->entry) + sizeof(log_msg->entry);
+ *msg = buf.prio;
+ fd = atomic_load(&logger_list->fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+ if (ret < 0) {
+ return -errno;
+ }
+ if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+
+ log_msg->entry.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+ log_msg->entry.hdr_size = sizeof(log_msg->entry);
+ log_msg->entry.pid = buf.p.pid;
+ log_msg->entry.tid = buf.l.tid;
+ log_msg->entry.sec = buf.l.realtime.tv_sec;
+ log_msg->entry.nsec = buf.l.realtime.tv_nsec;
+ log_msg->entry.lid = buf.l.id;
+ log_msg->entry.uid = buf.p.uid;
+
+ return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
+ }
+
+ fd = atomic_load(&logger_list->fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ current = TEMP_FAILURE_RETRY(lseek(fd, (off_t)0, SEEK_CUR));
+ if (current < 0) {
+ return -errno;
+ }
+ fd = atomic_load(&logger_list->fd);
+ if (fd <= 0) {
+ return -EBADF;
+ }
+ next = TEMP_FAILURE_RETRY(lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
+ if (next < 0) {
+ return -errno;
+ }
+ if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
+ return -EIO;
+ }
+ }
+}
+
+void PmsgClose(struct logger_list* logger_list) {
+ int fd = atomic_exchange(&logger_list->fd, 0);
+ if (fd > 0) {
+ close(fd);
+ }
+}
+
+static void* realloc_or_free(void* ptr, size_t new_size) {
+ void* result = realloc(ptr, new_size);
+ if (!result) {
+ free(ptr);
+ }
+ return result;
+}
+
+ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
+ __android_log_pmsg_file_read_fn fn, void* arg) {
+ ssize_t ret;
+ struct logger_list logger_list;
+ struct content {
+ struct listnode node;
+ struct logger_entry entry;
+ } * content;
+ struct names {
+ struct listnode node;
+ struct listnode content;
+ log_id_t id;
+ char prio;
+ char name[];
+ } * names;
+ struct listnode name_list;
+ struct listnode *node, *n;
+ size_t len, prefix_len;
+
+ if (!fn) {
+ return -EINVAL;
+ }
+
+ /* Add just enough clues in logger_list and transp to make API function */
+ memset(&logger_list, 0, sizeof(logger_list));
+
+ logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
+ logger_list.log_mask = (unsigned)-1;
+ if (logId != LOG_ID_ANY) {
+ logger_list.log_mask = (1 << logId);
+ }
+ logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+ if (!logger_list.log_mask) {
+ return -EINVAL;
+ }
+
+ /* Initialize name list */
+ list_init(&name_list);
+
+ ret = SSIZE_MAX;
+
+ /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
+ prefix_len = 0;
+ if (prefix) {
+ const char *prev = NULL, *last = NULL, *cp = prefix;
+ while ((cp = strpbrk(cp, "/:"))) {
+ prev = last;
+ last = cp;
+ cp = cp + 1;
+ }
+ if (prev) {
+ prefix = prev + 1;
+ }
+ prefix_len = strlen(prefix);
+ }
+
+ /* Read the file content */
+ log_msg log_msg;
+ while (PmsgRead(&logger_list, &log_msg) > 0) {
+ const char* cp;
+ size_t hdr_size = log_msg.entry.hdr_size;
+
+ char* msg = (char*)&log_msg + hdr_size;
+ const char* split = NULL;
+
+ if (hdr_size != sizeof(log_msg.entry)) {
+ continue;
+ }
+ /* Check for invalid sequence number */
+ if (log_msg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE ||
+ (log_msg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+ ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+ continue;
+ }
+
+ /* Determine if it has <dirbase>:<filebase> format for tag */
+ len = log_msg.entry.len - sizeof(prio);
+ for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
+ if (*cp == ':') {
+ if (split) {
+ break;
+ }
+ split = cp;
+ }
+ }
+ if (*cp || !split) {
+ continue;
+ }
+
+ /* Filters */
+ if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
+ size_t offset;
+ /*
+ * Allow : to be a synonym for /
+ * Things we do dealing with const char * and do not alloc
+ */
+ split = strchr(prefix, ':');
+ if (split) {
+ continue;
+ }
+ split = strchr(prefix, '/');
+ if (!split) {
+ continue;
+ }
+ offset = split - prefix;
+ if ((msg[offset + sizeof(prio)] != ':') || strncmp(msg + sizeof(prio), prefix, offset)) {
+ continue;
+ }
+ ++offset;
+ if ((prefix_len > offset) &&
+ strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
+ continue;
+ }
+ }
+
+ if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
+ continue;
+ }
+
+ /* check if there is an existing entry */
+ list_for_each(node, &name_list) {
+ names = node_to_item(node, struct names, node);
+ if (!strcmp(names->name, msg + sizeof(prio)) && names->id == log_msg.entry.lid &&
+ names->prio == *msg) {
+ break;
+ }
+ }
+
+ /* We do not have an existing entry, create and add one */
+ if (node == &name_list) {
+ static const char numbers[] = "0123456789";
+ unsigned long long nl;
+
+ len = strlen(msg + sizeof(prio)) + 1;
+ names = static_cast<struct names*>(calloc(1, sizeof(*names) + len));
+ if (!names) {
+ ret = -ENOMEM;
+ break;
+ }
+ strcpy(names->name, msg + sizeof(prio));
+ names->id = static_cast<log_id_t>(log_msg.entry.lid);
+ names->prio = *msg;
+ list_init(&names->content);
+ /*
+ * Insert in reverse numeric _then_ alpha sorted order as
+ * representative of log rotation:
+ *
+ * log.10
+ * klog.10
+ * . . .
+ * log.2
+ * klog.2
+ * log.1
+ * klog.1
+ * log
+ * klog
+ *
+ * thus when we present the content, we are provided the oldest
+ * first, which when 'refreshed' could spill off the end of the
+ * pmsg FIFO but retaining the newest data for last with best
+ * chances to survive.
+ */
+ nl = 0;
+ cp = strpbrk(names->name, numbers);
+ if (cp) {
+ nl = strtoull(cp, NULL, 10);
+ }
+ list_for_each_reverse(node, &name_list) {
+ struct names* a_name = node_to_item(node, struct names, node);
+ const char* r = a_name->name;
+ int compare = 0;
+
+ unsigned long long nr = 0;
+ cp = strpbrk(r, numbers);
+ if (cp) {
+ nr = strtoull(cp, NULL, 10);
+ }
+ if (nr != nl) {
+ compare = (nl > nr) ? 1 : -1;
+ }
+ if (compare == 0) {
+ compare = strcmp(names->name, r);
+ }
+ if (compare <= 0) {
+ break;
+ }
+ }
+ list_add_head(node, &names->node);
+ }
+
+ /* Remove any file fragments that match our sequence number */
+ list_for_each_safe(node, n, &names->content) {
+ content = node_to_item(node, struct content, node);
+ if (log_msg.entry.nsec == content->entry.nsec) {
+ list_remove(&content->node);
+ free(content);
+ }
+ }
+
+ /* Add content */
+ content = static_cast<struct content*>(
+ calloc(1, sizeof(content->node) + hdr_size + log_msg.entry.len));
+ if (!content) {
+ ret = -ENOMEM;
+ break;
+ }
+ memcpy(&content->entry, &log_msg.entry, hdr_size + log_msg.entry.len);
+
+ /* Insert in sequence number sorted order, to ease reconstruction */
+ list_for_each_reverse(node, &names->content) {
+ if ((node_to_item(node, struct content, node))->entry.nsec < log_msg.entry.nsec) {
+ break;
+ }
+ }
+ list_add_head(node, &content->node);
+ }
+ PmsgClose(&logger_list);
+
+ /* Progress through all the collected files */
+ list_for_each_safe(node, n, &name_list) {
+ struct listnode *content_node, *m;
+ char* buf;
+ size_t sequence, tag_len;
+
+ names = node_to_item(node, struct names, node);
+
+ /* Construct content into a linear buffer */
+ buf = NULL;
+ len = 0;
+ sequence = 0;
+ tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
+ list_for_each_safe(content_node, m, &names->content) {
+ ssize_t add_len;
+
+ content = node_to_item(content_node, struct content, node);
+ add_len = content->entry.len - tag_len - sizeof(prio);
+ if (add_len <= 0) {
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+
+ if (!buf) {
+ buf = static_cast<char*>(malloc(sizeof(char)));
+ if (!buf) {
+ ret = -ENOMEM;
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+ *buf = '\0';
+ }
+
+ /* Missing sequence numbers */
+ while (sequence < content->entry.nsec) {
+ /* plus space for enforced nul */
+ buf = static_cast<char*>(realloc_or_free(buf, len + sizeof(char) + sizeof(char)));
+ if (!buf) {
+ break;
+ }
+ buf[len] = '\f'; /* Mark missing content with a form feed */
+ buf[++len] = '\0';
+ sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
+ }
+ if (!buf) {
+ ret = -ENOMEM;
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+ /* plus space for enforced nul */
+ buf = static_cast<char*>(realloc_or_free(buf, len + add_len + sizeof(char)));
+ if (!buf) {
+ ret = -ENOMEM;
+ list_remove(content_node);
+ free(content);
+ continue;
+ }
+ memcpy(buf + len, (char*)&content->entry + content->entry.hdr_size + tag_len + sizeof(prio),
+ add_len);
+ len += add_len;
+ buf[len] = '\0'; /* enforce trailing hidden nul */
+ sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
+
+ list_remove(content_node);
+ free(content);
+ }
+ if (buf) {
+ if (len) {
+ /* Buffer contains enforced trailing nul just beyond length */
+ ssize_t r;
+ *strchr(names->name, ':') = '/'; /* Convert back to filename */
+ r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
+ if ((ret >= 0) && (r > 0)) {
+ if (ret == SSIZE_MAX) {
+ ret = r;
+ } else {
+ ret += r;
+ }
+ } else if (r < ret) {
+ ret = r;
+ }
+ }
+ free(buf);
+ }
+ list_remove(node);
+ free(names);
+ }
+ return (ret == SSIZE_MAX) ? -ENOENT : ret;
+}
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
new file mode 100644
index 000000000..b784f9ff5
--- /dev/null
+++ b/liblog/pmsg_reader.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include "log/log_read.h"
+
+__BEGIN_DECLS
+
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void PmsgClose(struct logger_list* logger_list);
+
+__END_DECLS
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
new file mode 100644
index 000000000..0751e2c53
--- /dev/null
+++ b/liblog/pmsg_writer.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pmsg_writer.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <log/log_properties.h>
+#include <private/android_logger.h>
+
+#include "logger.h"
+#include "uio.h"
+
+static atomic_int pmsg_fd;
+
+// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+ if (pmsg_fd != 0) {
+ return;
+ }
+
+ int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (new_fd <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+ close(new_fd);
+ return;
+ }
+}
+
+void PmsgClose() {
+ if (pmsg_fd > 0) {
+ close(pmsg_fd);
+ }
+ pmsg_fd = 0;
+}
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
+ static const unsigned headerLength = 2;
+ struct iovec newVec[nr + headerLength];
+ android_log_header_t header;
+ android_pmsg_log_header_t pmsgHeader;
+ size_t i, payloadSize;
+ ssize_t ret;
+
+ if (!__android_log_is_debuggable()) {
+ if (logId != LOG_ID_EVENTS && logId != LOG_ID_SECURITY) {
+ return -1;
+ }
+
+ if (logId == LOG_ID_EVENTS) {
+ if (vec[0].iov_len < 4) {
+ return -EINVAL;
+ }
+
+ if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
+ return -EPERM;
+ }
+ }
+ }
+
+ GetPmsgFd();
+
+ if (pmsg_fd <= 0) {
+ return -EBADF;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsgHeader;
+ * // what we provide to file
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ pmsgHeader.magic = LOGGER_MAGIC;
+ pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
+ pmsgHeader.uid = getuid();
+ pmsgHeader.pid = getpid();
+
+ header.id = logId;
+ header.tid = gettid();
+ header.realtime.tv_sec = ts->tv_sec;
+ header.realtime.tv_nsec = ts->tv_nsec;
+
+ newVec[0].iov_base = (unsigned char*)&pmsgHeader;
+ newVec[0].iov_len = sizeof(pmsgHeader);
+ newVec[1].iov_base = (unsigned char*)&header;
+ newVec[1].iov_len = sizeof(header);
+
+ for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+ newVec[i].iov_base = vec[i - headerLength].iov_base;
+ payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+ if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+ newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+ if (newVec[i].iov_len) {
+ ++i;
+ }
+ payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
+ break;
+ }
+ }
+ pmsgHeader.len += payloadSize;
+
+ ret = TEMP_FAILURE_RETRY(writev(pmsg_fd, newVec, i));
+ if (ret < 0) {
+ ret = errno ? -errno : -ENOTCONN;
+ }
+
+ if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
+ ret -= sizeof(header) - sizeof(pmsgHeader);
+ }
+
+ return ret;
+}
+
+/*
+ * Virtual pmsg filesystem
+ *
+ * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
+ * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
+ * file.
+ *
+ * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
+ */
+
+static inline const char* strnrchr(const char* buf, size_t len, char c) {
+ const char* cp = buf + len;
+ while ((--cp > buf) && (*cp != c))
+ ;
+ if (cp <= buf) {
+ return buf + len;
+ }
+ return cp;
+}
+
+/* Write a buffer as filename references (tag = <basedir>:<basename>) */
+ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
+ const char* buf, size_t len) {
+ size_t length, packet_len;
+ const char* tag;
+ char *cp, *slash;
+ struct timespec ts;
+ struct iovec vec[3];
+
+ /* Make sure the logId value is not a bad idea */
+ if ((logId == LOG_ID_KERNEL) || /* Verbotten */
+ (logId == LOG_ID_EVENTS) || /* Do not support binary content */
+ (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
+ ((unsigned)logId >= 32)) { /* fit within logMask on arch32 */
+ return -EINVAL;
+ }
+
+ clock_gettime(android_log_clockid(), &ts);
+
+ cp = strdup(filename);
+ if (!cp) {
+ return -ENOMEM;
+ }
+
+ tag = cp;
+ slash = strrchr(cp, '/');
+ if (slash) {
+ *slash = ':';
+ slash = strrchr(cp, '/');
+ if (slash) {
+ tag = slash + 1;
+ }
+ }
+
+ length = strlen(tag) + 1;
+ packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
+
+ vec[0].iov_base = &prio;
+ vec[0].iov_len = sizeof(char);
+ vec[1].iov_base = (unsigned char*)tag;
+ vec[1].iov_len = length;
+
+ for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+ ssize_t ret;
+ size_t transfer;
+
+ if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+ len -= length;
+ break;
+ }
+
+ transfer = length;
+ if (transfer > packet_len) {
+ transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
+ if ((transfer < length) && (buf[transfer] == '\n')) {
+ ++transfer;
+ }
+ }
+
+ vec[2].iov_base = (unsigned char*)buf;
+ vec[2].iov_len = transfer;
+
+ ret = PmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+
+ if (ret <= 0) {
+ free(cp);
+ return ret ? ret : (len - length);
+ }
+ length -= transfer;
+ buf += transfer;
+ }
+ free(cp);
+ return len;
+}
diff --git a/liblog/pmsg_writer.h b/liblog/pmsg_writer.h
new file mode 100644
index 000000000..d5e1a1c2f
--- /dev/null
+++ b/liblog/pmsg_writer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#include <android/log.h>
+
+int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
+void PmsgClose();
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
new file mode 100644
index 000000000..37670ecd6
--- /dev/null
+++ b/liblog/properties.cpp
@@ -0,0 +1,668 @@
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <log/log_properties.h>
+
+#include <ctype.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <private/android_logger.h>
+
+#include "logger_write.h"
+
+#ifdef __ANDROID__
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
+
+static int lock() {
+ /*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler logs a message, we could get into a deadlock state.
+ */
+ /*
+ * Any contention, and we can turn around and use the non-cached method
+ * in less time than the system call associated with a mutex to deal with
+ * the contention.
+ */
+ return pthread_mutex_trylock(&lock_loggable);
+}
+
+static void unlock() {
+ pthread_mutex_unlock(&lock_loggable);
+}
+
+struct cache {
+ const prop_info* pinfo;
+ uint32_t serial;
+};
+
+struct cache_char {
+ struct cache cache;
+ unsigned char c;
+};
+
+static int check_cache(struct cache* cache) {
+ return cache->pinfo && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
+#define BOOLEAN_TRUE 0xFF
+#define BOOLEAN_FALSE 0xFE
+
+static void refresh_cache(struct cache_char* cache, const char* key) {
+ char buf[PROP_VALUE_MAX];
+
+ if (!cache->cache.pinfo) {
+ cache->cache.pinfo = __system_property_find(key);
+ if (!cache->cache.pinfo) {
+ return;
+ }
+ }
+ cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+ __system_property_read(cache->cache.pinfo, 0, buf);
+ switch (buf[0]) {
+ case 't':
+ case 'T':
+ cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
+ break;
+ case 'f':
+ case 'F':
+ cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
+ break;
+ default:
+ cache->c = buf[0];
+ }
+}
+
+static int __android_log_level(const char* tag, size_t len) {
+ /* sizeof() is used on this array below */
+ static const char log_namespace[] = "persist.log.tag.";
+ static const size_t base_offset = 8; /* skip "persist." */
+
+ if (tag == nullptr || len == 0) {
+ auto& tag_string = GetDefaultTag();
+ tag = tag_string.c_str();
+ len = tag_string.size();
+ }
+
+ /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
+ char key[sizeof(log_namespace) + len];
+ char* kp;
+ size_t i;
+ char c = 0;
+ /*
+ * Single layer cache of four properties. Priorities are:
+ * log.tag.<tag>
+ * persist.log.tag.<tag>
+ * log.tag
+ * persist.log.tag
+ * Where the missing tag matches all tags and becomes the
+ * system global default. We do not support ro.log.tag* .
+ */
+ static char* last_tag;
+ static size_t last_tag_len;
+ static uint32_t global_serial;
+ /* some compilers erroneously see uninitialized use. !not_locked */
+ uint32_t current_global_serial = 0;
+ static struct cache_char tag_cache[2];
+ static struct cache_char global_cache[2];
+ int change_detected;
+ int global_change_detected;
+ int not_locked;
+
+ strcpy(key, log_namespace);
+
+ global_change_detected = change_detected = not_locked = lock();
+
+ if (!not_locked) {
+ /*
+ * check all known serial numbers to changes.
+ */
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ if (check_cache(&tag_cache[i].cache)) {
+ change_detected = 1;
+ }
+ }
+ for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ if (check_cache(&global_cache[i].cache)) {
+ global_change_detected = 1;
+ }
+ }
+
+ current_global_serial = __system_property_area_serial();
+ if (current_global_serial != global_serial) {
+ change_detected = 1;
+ global_change_detected = 1;
+ }
+ }
+
+ if (len) {
+ int local_change_detected = change_detected;
+ if (!not_locked) {
+ if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
+ strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
+ /* invalidate log.tag.<tag> cache */
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ tag_cache[i].cache.pinfo = NULL;
+ tag_cache[i].c = '\0';
+ }
+ if (last_tag) last_tag[0] = '\0';
+ local_change_detected = 1;
+ }
+ if (!last_tag || !last_tag[0]) {
+ if (!last_tag) {
+ last_tag = static_cast<char*>(calloc(1, len + 1));
+ last_tag_len = 0;
+ if (last_tag) last_tag_len = len + 1;
+ } else if (len >= last_tag_len) {
+ last_tag = static_cast<char*>(realloc(last_tag, len + 1));
+ last_tag_len = 0;
+ if (last_tag) last_tag_len = len + 1;
+ }
+ if (last_tag) {
+ strncpy(last_tag, tag, len);
+ last_tag[len] = '\0';
+ }
+ }
+ }
+ strncpy(key + sizeof(log_namespace) - 1, tag, len);
+ key[sizeof(log_namespace) - 1 + len] = '\0';
+
+ kp = key;
+ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+ struct cache_char* cache = &tag_cache[i];
+ struct cache_char temp_cache;
+
+ if (not_locked) {
+ temp_cache.cache.pinfo = NULL;
+ temp_cache.c = '\0';
+ cache = &temp_cache;
+ }
+ if (local_change_detected) {
+ refresh_cache(cache, kp);
+ }
+
+ if (cache->c) {
+ c = cache->c;
+ break;
+ }
+
+ kp = key + base_offset;
+ }
+ }
+
+ switch (toupper(c)) { /* if invalid, resort to global */
+ case 'V':
+ case 'D':
+ case 'I':
+ case 'W':
+ case 'E':
+ case 'F': /* Not officially supported */
+ case 'A':
+ case 'S':
+ case BOOLEAN_FALSE: /* Not officially supported */
+ break;
+ default:
+ /* clear '.' after log.tag */
+ key[sizeof(log_namespace) - 2] = '\0';
+
+ kp = key;
+ for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+ struct cache_char* cache = &global_cache[i];
+ struct cache_char temp_cache;
+
+ if (not_locked) {
+ temp_cache = *cache;
+ if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
+ temp_cache.cache.pinfo = NULL;
+ temp_cache.c = '\0';
+ }
+ cache = &temp_cache;
+ }
+ if (global_change_detected) {
+ refresh_cache(cache, kp);
+ }
+
+ if (cache->c) {
+ c = cache->c;
+ break;
+ }
+
+ kp = key + base_offset;
+ }
+ break;
+ }
+
+ if (!not_locked) {
+ global_serial = current_global_serial;
+ unlock();
+ }
+
+ switch (toupper(c)) {
+ /* clang-format off */
+ case 'V': return ANDROID_LOG_VERBOSE;
+ case 'D': return ANDROID_LOG_DEBUG;
+ case 'I': return ANDROID_LOG_INFO;
+ case 'W': return ANDROID_LOG_WARN;
+ case 'E': return ANDROID_LOG_ERROR;
+ case 'F': /* FALLTHRU */ /* Not officially supported */
+ case 'A': return ANDROID_LOG_FATAL;
+ case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
+ case 'S': return ANDROID_LOG_SILENT;
+ /* clang-format on */
+ }
+ return -1;
+}
+
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
+ int minimum_log_priority = __android_log_get_minimum_priority();
+ int property_log_level = __android_log_level(tag, len);
+
+ if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
+ return prio >= std::min(property_log_level, minimum_log_priority);
+ } else if (property_log_level >= 0) {
+ return prio >= property_log_level;
+ } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
+ return prio >= minimum_log_priority;
+ } else {
+ return prio >= default_prio;
+ }
+}
+
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
+ auto len = tag ? strlen(tag) : 0;
+ return __android_log_is_loggable_len(prio, tag, len, default_prio);
+}
+
+int __android_log_is_debuggable() {
+ static uint32_t serial;
+ static struct cache_char tag_cache;
+ static const char key[] = "ro.debuggable";
+ int ret;
+
+ if (tag_cache.c) { /* ro property does not change after set */
+ ret = tag_cache.c == '1';
+ } else if (lock()) {
+ struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
+ refresh_cache(&temp_cache, key);
+ ret = temp_cache.c == '1';
+ } else {
+ int change_detected = check_cache(&tag_cache.cache);
+ uint32_t current_serial = __system_property_area_serial();
+ if (current_serial != serial) {
+ change_detected = 1;
+ }
+ if (change_detected) {
+ refresh_cache(&tag_cache, key);
+ serial = current_serial;
+ }
+ ret = tag_cache.c == '1';
+
+ unlock();
+ }
+
+ return ret;
+}
+
+/*
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
+ */
+struct cache2_char {
+ pthread_mutex_t lock;
+ uint32_t serial;
+ const char* key_persist;
+ struct cache_char cache_persist;
+ const char* key_ro;
+ struct cache_char cache_ro;
+ unsigned char (*const evaluate)(const struct cache2_char* self);
+};
+
+static inline unsigned char do_cache2_char(struct cache2_char* self) {
+ uint32_t current_serial;
+ int change_detected;
+ unsigned char c;
+
+ if (pthread_mutex_trylock(&self->lock)) {
+ /* We are willing to accept some race in this context */
+ return self->evaluate(self);
+ }
+
+ change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
+ current_serial = __system_property_area_serial();
+ if (current_serial != self->serial) {
+ change_detected = 1;
+ }
+ if (change_detected) {
+ refresh_cache(&self->cache_persist, self->key_persist);
+ refresh_cache(&self->cache_ro, self->key_ro);
+ self->serial = current_serial;
+ }
+ c = self->evaluate(self);
+
+ pthread_mutex_unlock(&self->lock);
+
+ return c;
+}
+
+static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
+ unsigned char c = self->cache_persist.c;
+
+ if (c) {
+ return c;
+ }
+
+ return self->cache_ro.c;
+}
+
+/*
+ * Timestamp state generally remains constant, but can change at any time
+ * to handle developer requirements.
+ */
+clockid_t android_log_clockid() {
+ static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
+ "persist.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
+ "ro.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
+ evaluate_persist_ro};
+
+ return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2_char* self) {
+ unsigned char c = self->cache_ro.c;
+
+ return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
+
+int __android_log_security() {
+ static struct cache2_char security = {
+ PTHREAD_MUTEX_INITIALIZER, 0,
+ "persist.logd.security", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+ "ro.organization_owned", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+ evaluate_security};
+
+ return do_cache2_char(&security);
+}
+
+/*
+ * Interface that represents the logd buffer size determination so that others
+ * need not guess our intentions.
+ */
+
+/* Property helper */
+static bool check_flag(const char* prop, const char* flag) {
+ const char* cp = strcasestr(prop, flag);
+ if (!cp) {
+ return false;
+ }
+ /* We only will document comma (,) */
+ static const char sep[] = ",:;|+ \t\f";
+ if ((cp != prop) && !strchr(sep, cp[-1])) {
+ return false;
+ }
+ cp += strlen(flag);
+ return !*cp || !!strchr(sep, *cp);
+}
+
+/* cache structure */
+struct cache_property {
+ struct cache cache;
+ char property[PROP_VALUE_MAX];
+};
+
+static void refresh_cache_property(struct cache_property* cache, const char* key) {
+ if (!cache->cache.pinfo) {
+ cache->cache.pinfo = __system_property_find(key);
+ if (!cache->cache.pinfo) {
+ return;
+ }
+ }
+ cache->cache.serial = __system_property_serial(cache->cache.pinfo);
+ __system_property_read(cache->cache.pinfo, 0, cache->property);
+}
+
+/* get boolean with the logger twist that supports eng adjustments */
+bool __android_logger_property_get_bool(const char* key, int flag) {
+ struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
+ if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+ char newkey[strlen("persist.") + strlen(key) + 1];
+ snprintf(newkey, sizeof(newkey), "ro.%s", key);
+ refresh_cache_property(&property, newkey);
+ property.cache.pinfo = NULL;
+ property.cache.serial = 0xFFFFFFFF;
+ snprintf(newkey, sizeof(newkey), "persist.%s", key);
+ refresh_cache_property(&property, newkey);
+ property.cache.pinfo = NULL;
+ property.cache.serial = 0xFFFFFFFF;
+ }
+
+ refresh_cache_property(&property, key);
+
+ if (check_flag(property.property, "true")) {
+ return true;
+ }
+ if (check_flag(property.property, "false")) {
+ return false;
+ }
+ if (property.property[0]) {
+ flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ }
+ if (check_flag(property.property, "eng")) {
+ flag |= BOOL_DEFAULT_FLAG_ENG;
+ }
+ /* this is really a "not" flag */
+ if (check_flag(property.property, "svelte")) {
+ flag |= BOOL_DEFAULT_FLAG_SVELTE;
+ }
+
+ /* Sanity Check */
+ if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+ flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+ flag |= BOOL_DEFAULT_TRUE;
+ }
+
+ if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
+ __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
+ return false;
+ }
+ if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
+ return false;
+ }
+
+ return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long value) {
+ static long pages, pagesize;
+ unsigned long maximum;
+
+ if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
+ return false;
+ }
+
+ if (!pages) {
+ pages = sysconf(_SC_PHYS_PAGES);
+ }
+ if (pages < 1) {
+ return true;
+ }
+
+ if (!pagesize) {
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize <= 1) {
+ pagesize = PAGE_SIZE;
+ }
+ }
+
+ /* maximum memory impact a somewhat arbitrary ~3% */
+ pages = (pages + 31) / 32;
+ maximum = pages * pagesize;
+
+ if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
+ return true;
+ }
+
+ return value <= maximum;
+}
+
+struct cache2_property_size {
+ pthread_mutex_t lock;
+ uint32_t serial;
+ const char* key_persist;
+ struct cache_property cache_persist;
+ const char* key_ro;
+ struct cache_property cache_ro;
+ unsigned long (*const evaluate)(const struct cache2_property_size* self);
+};
+
+static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) {
+ uint32_t current_serial;
+ int change_detected;
+ unsigned long v;
+
+ if (pthread_mutex_trylock(&self->lock)) {
+ /* We are willing to accept some race in this context */
+ return self->evaluate(self);
+ }
+
+ change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
+ current_serial = __system_property_area_serial();
+ if (current_serial != self->serial) {
+ change_detected = 1;
+ }
+ if (change_detected) {
+ refresh_cache_property(&self->cache_persist, self->key_persist);
+ refresh_cache_property(&self->cache_ro, self->key_ro);
+ self->serial = current_serial;
+ }
+ v = self->evaluate(self);
+
+ pthread_mutex_unlock(&self->lock);
+
+ return v;
+}
+
+static unsigned long property_get_size_from_cache(const struct cache_property* cache) {
+ char* cp;
+ unsigned long value = strtoul(cache->property, &cp, 10);
+
+ switch (*cp) {
+ case 'm':
+ case 'M':
+ value *= 1024;
+ [[fallthrough]];
+ case 'k':
+ case 'K':
+ value *= 1024;
+ [[fallthrough]];
+ case '\0':
+ break;
+
+ default:
+ value = 0;
+ }
+
+ if (!__android_logger_valid_buffer_size(value)) {
+ value = 0;
+ }
+
+ return value;
+}
+
+static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) {
+ unsigned long size = property_get_size_from_cache(&self->cache_persist);
+ if (size) {
+ return size;
+ }
+ return property_get_size_from_cache(&self->cache_ro);
+}
+
+unsigned long __android_logger_get_buffer_size(log_id_t logId) {
+ static const char global_tunable[] = "persist.logd.size"; /* Settings App */
+ static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */
+ static struct cache2_property_size global = {
+ /* clang-format off */
+ PTHREAD_MUTEX_INITIALIZER, 0,
+ global_tunable, { { NULL, 0xFFFFFFFF }, {} },
+ global_default, { { NULL, 0xFFFFFFFF }, {} },
+ evaluate_property_get_size
+ /* clang-format on */
+ };
+ char key_persist[strlen(global_tunable) + strlen(".security") + 1];
+ char key_ro[strlen(global_default) + strlen(".security") + 1];
+ struct cache2_property_size local = {
+ /* clang-format off */
+ PTHREAD_MUTEX_INITIALIZER, 0,
+ key_persist, { { NULL, 0xFFFFFFFF }, {} },
+ key_ro, { { NULL, 0xFFFFFFFF }, {} },
+ evaluate_property_get_size
+ /* clang-format on */
+ };
+ unsigned long property_size, default_size;
+
+ default_size = do_cache2_property_size(&global);
+ if (!default_size) {
+ default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
+ ? LOG_BUFFER_MIN_SIZE /* 64K */
+ : LOG_BUFFER_SIZE; /* 256K */
+ }
+
+ snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
+ android_log_id_to_name(logId));
+ snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default, android_log_id_to_name(logId));
+ property_size = do_cache2_property_size(&local);
+
+ if (!property_size) {
+ property_size = default_size;
+ }
+
+ if (!property_size) {
+ property_size = LOG_BUFFER_SIZE;
+ }
+
+ return property_size;
+}
+
+#else
+
+int __android_log_is_loggable(int prio, const char*, int) {
+ int minimum_priority = __android_log_get_minimum_priority();
+ if (minimum_priority == ANDROID_LOG_DEFAULT) {
+ minimum_priority = ANDROID_LOG_INFO;
+ }
+ return prio >= minimum_priority;
+}
+
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
+ return __android_log_is_loggable(prio, nullptr, def);
+}
+
+int __android_log_is_debuggable() {
+ return 1;
+}
+
+#endif \ No newline at end of file
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
new file mode 100644
index 000000000..50800c543
--- /dev/null
+++ b/liblog/tests/Android.bp
@@ -0,0 +1,113 @@
+//
+// Copyright (C) 2013-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// -----------------------------------------------------------------------------
+// Benchmarks.
+// -----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+// adb shell liblog-benchmarks
+cc_benchmark {
+ name: "liblog-benchmarks",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+ shared_libs: [
+ "libm",
+ "libbase",
+ "libcutils",
+ ],
+ static_libs: ["liblog"],
+ srcs: ["liblog_benchmark.cpp"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "liblog-tests-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+ srcs: [
+ "libc_test.cpp",
+ "liblog_default_tag.cpp",
+ "liblog_global_state.cpp",
+ "liblog_test.cpp",
+ "log_id_test.cpp",
+ "log_radio_test.cpp",
+ "log_read_test.cpp",
+ "log_system_test.cpp",
+ "log_time_test.cpp",
+ "log_wrap_test.cpp",
+ "logd_writer_test.cpp",
+ "logprint_test.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ ],
+ static_libs: ["liblog"],
+ isolated: true,
+}
+
+// Build tests for the device (with .so). Run with:
+// adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
+cc_test {
+ name: "liblog-unit-tests",
+ defaults: ["liblog-tests-defaults"],
+}
+
+cc_test {
+ name: "CtsLiblogTestCases",
+ defaults: ["liblog-tests-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ cflags: ["-DNO_PSTORE"],
+ test_suites: [
+ "cts",
+ "vts10",
+ ],
+}
+
+cc_test_host {
+ name: "liblog-host-test",
+ static_libs: ["liblog"],
+ shared_libs: ["libbase"],
+ srcs: [
+ "liblog_host_test.cpp",
+ "liblog_default_tag.cpp",
+ "liblog_global_state.cpp",
+ ],
+ isolated: true,
+}
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
new file mode 100644
index 000000000..fcb46b1be
--- /dev/null
+++ b/liblog/tests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Logging Library test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsLiblogTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
new file mode 100644
index 000000000..3534eb8d3
--- /dev/null
+++ b/liblog/tests/libc_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+TEST(libc, __pstore_append) {
+#ifdef __ANDROID__
+#ifndef NO_PSTORE
+ FILE* fp;
+ ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
+ static const char message[] = "libc.__pstore_append\n";
+ ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
+ int fflushReturn = fflush(fp);
+ int fflushErrno = fflushReturn ? errno : 0;
+ ASSERT_EQ(0, fflushReturn);
+ ASSERT_EQ(0, fflushErrno);
+ int fcloseReturn = fclose(fp);
+ int fcloseErrno = fcloseReturn ? errno : 0;
+ ASSERT_EQ(0, fcloseReturn);
+ ASSERT_EQ(0, fcloseErrno);
+ if ((fcloseErrno == ENOMEM) || (fflushErrno == ENOMEM)) {
+ fprintf(stderr,
+ "Kernel does not have space allocated to pmsg pstore driver "
+ "configured\n");
+ }
+ if (!fcloseReturn && !fcloseErrno && !fflushReturn && !fflushReturn) {
+ fprintf(stderr,
+ "Reboot, ensure string libc.__pstore_append is in "
+ "/sys/fs/pstore/pmsg-ramoops-0\n");
+ }
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
new file mode 100644
index 000000000..39ac7a5f3
--- /dev/null
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) 2013-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <sched.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <cutils/sockets.h>
+#include <log/event_tag_map.h>
+#include <private/android_logger.h>
+
+BENCHMARK_MAIN();
+
+// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
+// non-syscall libs. Since we are benchmarking, or using this in the emergency
+// signal to stuff a terminating code, we do NOT want to introduce
+// a syscall or usleep on EAGAIN retry.
+#define LOG_FAILURE_RETRY(exp) \
+ ({ \
+ typeof(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) || \
+ (_rc == -EINTR) || (_rc == -EAGAIN)); \
+ _rc; \
+ })
+
+/*
+ * Measure the fastest rate we can reliabley stuff print messages into
+ * the log at high pressure. Expect this to be less than double the process
+ * wakeup time (2ms?)
+ */
+static void BM_log_maximum_retry(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_INFO, "BM_log_maximum_retry", "%" PRIu64,
+ state.iterations()));
+ }
+}
+BENCHMARK(BM_log_maximum_retry);
+
+/*
+ * Measure the fastest rate we can stuff print messages into the log
+ * at high pressure. Expect this to be less than double the process wakeup
+ * time (2ms?)
+ */
+static void BM_log_maximum(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%" PRIu64, state.iterations());
+ }
+}
+BENCHMARK(BM_log_maximum);
+
+/*
+ * Measure the time it takes to collect the time using
+ * discrete acquisition (state.PauseTiming() to state.ResumeTiming())
+ * under light load. Expect this to be a syscall period (2us) or
+ * data read time if zero-syscall.
+ *
+ * vdso support in the kernel and the library can allow
+ * clock_gettime to be zero-syscall, but there there does remain some
+ * benchmarking overhead to pause and resume; assumptions are both are
+ * covered.
+ */
+static void BM_clock_overhead(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ state.PauseTiming();
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_clock_overhead);
+
+static void do_clock_overhead(benchmark::State& state, clockid_t clk_id) {
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(clk_id, &t);
+ }
+}
+
+static void BM_time_clock_gettime_REALTIME(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_REALTIME);
+}
+BENCHMARK(BM_time_clock_gettime_REALTIME);
+
+static void BM_time_clock_gettime_MONOTONIC(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_MONOTONIC);
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC);
+
+static void BM_time_clock_gettime_MONOTONIC_syscall(benchmark::State& state) {
+ timespec t;
+ while (state.KeepRunning()) {
+ syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &t);
+ }
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC_syscall);
+
+static void BM_time_clock_gettime_MONOTONIC_RAW(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_MONOTONIC_RAW);
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC_RAW);
+
+static void BM_time_clock_gettime_BOOTTIME(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_BOOTTIME);
+}
+BENCHMARK(BM_time_clock_gettime_BOOTTIME);
+
+static void BM_time_clock_getres_MONOTONIC(benchmark::State& state) {
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_MONOTONIC, &t);
+ }
+}
+BENCHMARK(BM_time_clock_getres_MONOTONIC);
+
+static void BM_time_clock_getres_MONOTONIC_syscall(benchmark::State& state) {
+ timespec t;
+ while (state.KeepRunning()) {
+ syscall(__NR_clock_getres, CLOCK_MONOTONIC, &t);
+ }
+}
+BENCHMARK(BM_time_clock_getres_MONOTONIC_syscall);
+
+static void BM_time_time(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ time_t now;
+ now = time(&now);
+ }
+}
+BENCHMARK(BM_time_time);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ */
+static void BM_pmsg_short(benchmark::State& state) {
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_pmsg_log_header_t pmsg_header;
+ pmsg_header.magic = LOGGER_MAGIC;
+ pmsg_header.len =
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t);
+ pmsg_header.uid = getuid();
+ pmsg_header.pid = getpid();
+
+ android_log_header_t header;
+ header.tid = gettid();
+ header.realtime.tv_sec = ts.tv_sec;
+ header.realtime.tv_nsec = ts.tv_nsec;
+
+ static const unsigned nr = 1;
+ static const unsigned header_length = 2;
+ struct iovec newVec[nr + header_length];
+
+ newVec[0].iov_base = (unsigned char*)&pmsg_header;
+ newVec[0].iov_len = sizeof(pmsg_header);
+ newVec[1].iov_base = (unsigned char*)&header;
+ newVec[1].iov_len = sizeof(header);
+
+ android_log_event_int_t buffer;
+
+ header.id = LOG_ID_EVENTS;
+ buffer.header.tag = 0;
+ buffer.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer.payload.data = snapshot;
+
+ newVec[2].iov_base = &buffer;
+ newVec[2].iov_len = sizeof(buffer);
+
+ while (state.KeepRunning()) {
+ ++snapshot;
+ buffer.payload.data = snapshot;
+ writev(pstore_fd, newVec, nr);
+ }
+ state.PauseTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_aligned(benchmark::State& state) {
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8];
+ memset(buf, 0, sizeof(buf));
+ struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+ if (((uintptr_t)&buffer->pmsg_header) & 7) {
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
+ state.iterations());
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len =
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = snapshot;
+
+ while (state.KeepRunning()) {
+ ++snapshot;
+ buffer->payload.payload.data = snapshot;
+ write(pstore_fd, &buffer->pmsg_header,
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
+ sizeof(android_log_event_int_t));
+ }
+ state.PauseTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_unaligned1(benchmark::State& state) {
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8];
+ memset(buf, 0, sizeof(buf));
+ struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+ if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
+ state.iterations());
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len =
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = snapshot;
+
+ while (state.KeepRunning()) {
+ ++snapshot;
+ buffer->payload.payload.data = snapshot;
+ write(pstore_fd, &buffer->pmsg_header,
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
+ sizeof(android_log_event_int_t));
+ }
+ state.PauseTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_unaligned1);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_aligned(benchmark::State& state) {
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
+ memset(buf, 0, sizeof(buf));
+ struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+ if (((uintptr_t)&buffer->pmsg_header) & 7) {
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
+ state.iterations());
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len =
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = snapshot;
+
+ while (state.KeepRunning()) {
+ ++snapshot;
+ buffer->payload.payload.data = snapshot;
+ write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+ state.PauseTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_unaligned1(benchmark::State& state) {
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
+ return;
+ }
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsg_header;
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ struct packet {
+ android_pmsg_log_header_t pmsg_header;
+ android_log_header_t header;
+ android_log_event_int_t payload;
+ };
+ alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
+ memset(buf, 0, sizeof(buf));
+ struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+ if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
+ state.iterations());
+ }
+
+ buffer->pmsg_header.magic = LOGGER_MAGIC;
+ buffer->pmsg_header.len =
+ sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t);
+ buffer->pmsg_header.uid = getuid();
+ buffer->pmsg_header.pid = getpid();
+
+ buffer->header.tid = gettid();
+ buffer->header.realtime.tv_sec = ts.tv_sec;
+ buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+ buffer->header.id = LOG_ID_EVENTS;
+ buffer->payload.header.tag = 0;
+ buffer->payload.payload.type = EVENT_TYPE_INT;
+ uint32_t snapshot = 0;
+ buffer->payload.payload.data = snapshot;
+
+ while (state.KeepRunning()) {
+ ++snapshot;
+ buffer->payload.payload.data = snapshot;
+ write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+ }
+ state.PauseTiming();
+ close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_unaligned1);
+
+/*
+ * Measure the time it takes to form sprintf plus time using
+ * discrete acquisition under light load. Expect this to be a syscall period
+ * (2us) or sprintf time if zero-syscall time.
+ */
+/* helper function */
+static void test_print(const char* fmt, ...) {
+ va_list ap;
+ char buf[1024];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+}
+
+#define logd_yield() sched_yield() // allow logd to catch up
+#define logd_sleep() usleep(50) // really allow logd to catch up
+
+/* performance test */
+static void BM_sprintf_overhead(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ test_print("BM_sprintf_overhead:%" PRIu64, state.iterations());
+ state.PauseTiming();
+ logd_yield();
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_sprintf_overhead);
+
+/*
+ * Measure the time it takes to submit the android printing logging call
+ * using discrete acquisition discrete acquisition under light load. Expect
+ * this to be a dozen or so syscall periods (40us) plus time to run *printf
+ */
+static void BM_log_print_overhead(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%" PRIu64, state.iterations());
+ state.PauseTiming();
+ logd_yield();
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_log_print_overhead);
+
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition under light load. Expect this to be a long path
+ * to logger to convert the unknown tag (0) into a tagname (less than 200us).
+ */
+static void BM_log_event_overhead(benchmark::State& state) {
+ for (int64_t i = 0; state.KeepRunning(); ++i) {
+ // log tag number 0 is not known, nor shall it ever be known
+ __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
+ state.PauseTiming();
+ logd_yield();
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_log_event_overhead);
+
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition under light load with a known logtag. Expect
+ * this to be a dozen or so syscall periods (less than 40us)
+ */
+static void BM_log_event_overhead_42(benchmark::State& state) {
+ for (int64_t i = 0; state.KeepRunning(); ++i) {
+ // In system/core/logcat/event.logtags:
+ // # These are used for testing, do not modify without updating
+ // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+ // # system/core/liblog/tests/liblog_benchmark.cpp
+ // # system/core/liblog/tests/liblog_test.cpp
+ // 42 answer (to life the universe etc|3)
+ __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
+ state.PauseTiming();
+ logd_yield();
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_log_event_overhead_42);
+
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition under very-light load (<1% CPU utilization).
+ */
+static void BM_log_light_overhead(benchmark::State& state) {
+ for (int64_t i = 0; state.KeepRunning(); ++i) {
+ __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
+ state.PauseTiming();
+ usleep(10000);
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_log_light_overhead);
+
+static void caught_latency(int /*signum*/) {
+ unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+static unsigned long long caught_convert(char* cp) {
+ unsigned long long l = cp[0] & 0xFF;
+ l |= (unsigned long long)(cp[1] & 0xFF) << 8;
+ l |= (unsigned long long)(cp[2] & 0xFF) << 16;
+ l |= (unsigned long long)(cp[3] & 0xFF) << 24;
+ l |= (unsigned long long)(cp[4] & 0xFF) << 32;
+ l |= (unsigned long long)(cp[5] & 0xFF) << 40;
+ l |= (unsigned long long)(cp[6] & 0xFF) << 48;
+ l |= (unsigned long long)(cp[7] & 0xFF) << 56;
+ return l;
+}
+
+static const int alarm_time = 3;
+
+/*
+ * Measure the time it takes for the logd posting call to acquire the
+ * timestamp to place into the internal record. Expect this to be less than
+ * 4 syscalls (3us). This test uses manual injection of timing because it is
+ * comparing the timestamp at send, and then picking up the corresponding log
+ * end-to-end long path from logd to see what actual timestamp was submitted.
+ */
+static void BM_log_latency(benchmark::State& state) {
+ pid_t pid = getpid();
+
+ struct logger_list* logger_list =
+ android_logger_list_open(LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 0, pid);
+
+ if (!logger_list) {
+ fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGALRM, caught_latency);
+ alarm(alarm_time);
+
+ for (size_t j = 0; state.KeepRunning() && j < 10 * state.iterations(); ++j) {
+ retry: // We allow transitory errors (logd overloaded) to be retried.
+ log_time ts;
+ LOG_FAILURE_RETRY((ts = log_time(CLOCK_REALTIME),
+ android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))));
+
+ for (;;) {
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ alarm(alarm_time);
+
+ if (ret <= 0) {
+ state.SkipWithError("android_logger_list_read");
+ break;
+ }
+ if ((log_msg.entry.len != (4 + 1 + 8)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char* eventData = log_msg.msg();
+
+ if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+ continue;
+ }
+ log_time tx(eventData + 4 + 1);
+ if (ts != tx) {
+ if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
+ state.SkipWithError("signal");
+ break;
+ }
+ continue;
+ }
+
+ uint64_t start = ts.nsec();
+ uint64_t end = log_msg.nsec();
+ if (end < start) goto retry;
+ state.SetIterationTime((end - start) / (double)NS_PER_SEC);
+ break;
+ }
+ }
+
+ signal(SIGALRM, SIG_DFL);
+ alarm(0);
+
+ android_logger_list_free(logger_list);
+}
+// Default gets out of hand for this test, so we set a reasonable number of
+// iterations for a timely result.
+BENCHMARK(BM_log_latency)->UseManualTime()->Iterations(200);
+
+static void caught_delay(int /*signum*/) {
+ unsigned long long v = 0xDEADBEEFA55A5AA6ULL;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+/*
+ * Measure the time it takes for the logd posting call to make it into
+ * the logs. Expect this to be less than double the process wakeup time (2ms).
+ */
+static void BM_log_delay(benchmark::State& state) {
+ pid_t pid = getpid();
+
+ struct logger_list* logger_list =
+ android_logger_list_open(LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 0, pid);
+
+ if (!logger_list) {
+ fprintf(stderr, "Unable to open events log: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGALRM, caught_delay);
+ alarm(alarm_time);
+
+ while (state.KeepRunning()) {
+ log_time ts(CLOCK_REALTIME);
+
+ LOG_FAILURE_RETRY(android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ for (;;) {
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ alarm(alarm_time);
+
+ if (ret <= 0) {
+ state.SkipWithError("android_logger_list_read");
+ break;
+ }
+ if ((log_msg.entry.len != (4 + 1 + 8)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char* eventData = log_msg.msg();
+
+ if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+ continue;
+ }
+ log_time tx(eventData + 4 + 1);
+ if (ts != tx) {
+ if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
+ state.SkipWithError("signal");
+ break;
+ }
+ continue;
+ }
+
+ break;
+ }
+ }
+ state.PauseTiming();
+
+ signal(SIGALRM, SIG_DFL);
+ alarm(0);
+
+ android_logger_list_free(logger_list);
+}
+BENCHMARK(BM_log_delay);
+
+/*
+ * Measure the time it takes for __android_log_is_loggable.
+ */
+static void BM_is_loggable(benchmark::State& state) {
+ static const char logd[] = "logd";
+
+ while (state.KeepRunning()) {
+ __android_log_is_loggable_len(ANDROID_LOG_WARN, logd, strlen(logd),
+ ANDROID_LOG_VERBOSE);
+ }
+}
+BENCHMARK(BM_is_loggable);
+
+/*
+ * Measure the time it takes for android_log_clockid.
+ */
+static void BM_clockid(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ android_log_clockid();
+ }
+}
+BENCHMARK(BM_clockid);
+
+/*
+ * Measure the time it takes for __android_log_security.
+ */
+static void BM_security(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ __android_log_security();
+ }
+}
+BENCHMARK(BM_security);
+
+// Keep maps around for multiple iterations
+static std::unordered_set<uint32_t> set;
+static EventTagMap* map;
+
+static bool prechargeEventMap() {
+ if (map) return true;
+
+ fprintf(stderr, "Precharge: start\n");
+
+ map = android_openEventTagMap(NULL);
+ for (uint32_t tag = 1; tag < USHRT_MAX; ++tag) {
+ size_t len;
+ if (android_lookupEventTag_len(map, &len, tag) == NULL) continue;
+ set.insert(tag);
+ }
+
+ fprintf(stderr, "Precharge: stop %zu\n", set.size());
+
+ return true;
+}
+
+/*
+ * Measure the time it takes for android_lookupEventTag_len
+ */
+static void BM_lookupEventTag(benchmark::State& state) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ while (state.KeepRunning()) {
+ size_t len;
+ android_lookupEventTag_len(map, &len, (*it));
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventTag);
+
+/*
+ * Measure the time it takes for android_lookupEventTag_len
+ */
+static uint32_t notTag = 1;
+
+static void BM_lookupEventTag_NOT(benchmark::State& state) {
+ prechargeEventMap();
+
+ while (set.find(notTag) != set.end()) {
+ ++notTag;
+ if (notTag >= USHRT_MAX) notTag = 1;
+ }
+
+ while (state.KeepRunning()) {
+ size_t len;
+ android_lookupEventTag_len(map, &len, notTag);
+ }
+
+ ++notTag;
+ if (notTag >= USHRT_MAX) notTag = 1;
+}
+BENCHMARK(BM_lookupEventTag_NOT);
+
+/*
+ * Measure the time it takes for android_lookupEventFormat_len
+ */
+static void BM_lookupEventFormat(benchmark::State& state) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ while (state.KeepRunning()) {
+ size_t len;
+ android_lookupEventFormat_len(map, &len, (*it));
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventFormat);
+
+/*
+ * Measure the time it takes for android_lookupEventTagNum plus above
+ */
+static void BM_lookupEventTagNum(benchmark::State& state) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ while (state.KeepRunning()) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+ state.ResumeTiming();
+ android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
+ ANDROID_LOG_UNKNOWN);
+ state.PauseTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventTagNum);
+
+// Must be functionally identical to liblog internal SendLogdControlMessage()
+static void send_to_control(char* buf, size_t len) {
+ int sock =
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
+ if (sock < 0) return;
+ size_t writeLen = strlen(buf) + 1;
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, buf, writeLen));
+ if (ret <= 0) {
+ close(sock);
+ return;
+ }
+ while ((ret = read(sock, buf, len)) > 0) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+ break;
+ }
+ len -= ret;
+ buf += ret;
+
+ struct pollfd p = {.fd = sock, .events = POLLIN, .revents = 0 };
+
+ ret = poll(&p, 1, 20);
+ if ((ret <= 0) || !(p.revents & POLLIN)) {
+ break;
+ }
+ }
+ close(sock);
+}
+
+static void BM_lookupEventTagNum_logd_new(benchmark::State& state) {
+ fprintf(stderr,
+ "WARNING: "
+ "This test can cause logd to grow in size and hit DOS limiter\n");
+ // Make copies
+ static const char empty_event_log_tags[] = "# content owned by logd\n";
+ static const char dev_event_log_tags_path[] = "/dev/event-log-tags";
+ std::string dev_event_log_tags;
+ if (android::base::ReadFileToString(dev_event_log_tags_path,
+ &dev_event_log_tags) &&
+ (dev_event_log_tags.length() == 0)) {
+ dev_event_log_tags = empty_event_log_tags;
+ }
+ static const char data_event_log_tags_path[] =
+ "/data/misc/logd/event-log-tags";
+ std::string data_event_log_tags;
+ if (android::base::ReadFileToString(data_event_log_tags_path,
+ &data_event_log_tags) &&
+ (data_event_log_tags.length() == 0)) {
+ data_event_log_tags = empty_event_log_tags;
+ }
+
+ while (state.KeepRunning()) {
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"(new|1)\"",
+ name);
+ state.ResumeTiming();
+ send_to_control(buffer, sizeof(buffer));
+ state.PauseTiming();
+ }
+
+ // Restore copies (logd still know about them, until crash or reboot)
+ if (dev_event_log_tags.length() &&
+ !android::base::WriteStringToFile(dev_event_log_tags,
+ dev_event_log_tags_path)) {
+ fprintf(stderr,
+ "WARNING: "
+ "failed to restore %s\n",
+ dev_event_log_tags_path);
+ }
+ if (data_event_log_tags.length() &&
+ !android::base::WriteStringToFile(data_event_log_tags,
+ data_event_log_tags_path)) {
+ fprintf(stderr,
+ "WARNING: "
+ "failed to restore %s\n",
+ data_event_log_tags_path);
+ }
+ fprintf(stderr,
+ "WARNING: "
+ "Restarting logd to make it forget what we just did\n");
+ system("stop logd ; start logd");
+}
+BENCHMARK(BM_lookupEventTagNum_logd_new);
+
+static void BM_lookupEventTagNum_logd_existing(benchmark::State& state) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ while (state.KeepRunning()) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"%s\"",
+ Name.c_str(), Format.c_str());
+
+ state.ResumeTiming();
+ send_to_control(buffer, sizeof(buffer));
+ state.PauseTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventTagNum_logd_existing);
diff --git a/liblog/tests/liblog_default_tag.cpp b/liblog/tests/liblog_default_tag.cpp
new file mode 100644
index 000000000..31b746708
--- /dev/null
+++ b/liblog/tests/liblog_default_tag.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// LOG_TAG must be unset for android-base's logging to use a default tag.
+#undef LOG_TAG
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+#ifndef __ANDROID__
+static const char* getprogname() {
+ return program_invocation_short_name;
+}
+#endif
+
+TEST(liblog_default_tag, no_default_tag_libbase_write_first) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+
+ expected_tag = getprogname();
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, no_default_tag_liblog_write_first) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+
+ expected_tag = getprogname();
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, libbase_sets_default_tag) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "libbase_test_tag";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+ SetDefaultTag(expected_tag);
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, liblog_sets_default_tag) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "liblog_test_tag";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+ __android_log_set_default_tag(expected_tag.c_str());
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(WARNING) << "message";
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_default_tag, default_tag_plus_log_severity) {
+#ifdef __ANDROID__
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = "liblog_test_tag";
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+ __android_log_set_default_tag(expected_tag.c_str());
+
+ auto log_tag_property = "log.tag." + expected_tag;
+ SetProperty(log_tag_property, "V");
+ auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(VERBOSE) << "message";
+ EXPECT_TRUE(message_seen);
+#else
+ GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
+
+TEST(liblog_default_tag, generated_default_tag_plus_log_severity) {
+#ifdef __ANDROID__
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_tag = getprogname();
+ SetLogger([&](LogId, LogSeverity, const char* tag, const char*, unsigned int, const char*) {
+ message_seen = true;
+ EXPECT_EQ(expected_tag, tag);
+ });
+
+ // Even without any calls to SetDefaultTag(), the first message that attempts to log, will
+ // generate a default tag from getprogname() and check log.tag.<default tag> for loggability. This
+ // case checks that we can log a Verbose message when log.tag.<getprogname()> is set to 'V'.
+ auto log_tag_property = "log.tag." + expected_tag;
+ SetProperty(log_tag_property, "V");
+ auto reset_tag_property_guard = make_scope_guard([=] { SetProperty(log_tag_property, ""); });
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, nullptr, "message");
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ LOG(VERBOSE) << "message";
+ EXPECT_TRUE(message_seen);
+#else
+ GTEST_SKIP() << "No log tag properties on host";
+#endif
+} \ No newline at end of file
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
new file mode 100644
index 000000000..3508818f2
--- /dev/null
+++ b/liblog/tests/liblog_global_state.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "global_state_test_tag"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/log.h>
+
+#include <gtest/gtest.h>
+
+TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) {
+ using namespace android::base;
+ bool message_seen = false;
+ LogSeverity expected_severity = WARNING;
+ std::string expected_file = Basename(__FILE__);
+ unsigned int expected_line;
+ std::string expected_message = "libbase test message";
+
+ auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ message_seen = true;
+ EXPECT_EQ(DEFAULT, log_id);
+ EXPECT_EQ(expected_severity, severity);
+ EXPECT_STREQ(LOG_TAG, tag);
+ EXPECT_EQ(expected_file, file);
+ EXPECT_EQ(expected_line, line);
+ EXPECT_EQ(expected_message, message);
+ };
+
+ SetLogger(LoggerFunction);
+
+ expected_line = __LINE__ + 1;
+ LOG(expected_severity) << expected_message;
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) {
+ using namespace android::base;
+ // These must be static since they're used by the liblog logger function, which only accepts
+ // lambdas without captures. The items used by the libbase logger are explicitly not static, to
+ // ensure that lambdas with captures do work there.
+ static bool message_seen = false;
+ static std::string expected_file = Basename(__FILE__);
+ static unsigned int expected_line;
+ static std::string expected_message = "libbase test message";
+
+ auto liblog_logger_function = [](const struct __android_log_message* log_message) {
+ message_seen = true;
+ EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
+ EXPECT_EQ(LOG_ID_DEFAULT, log_message->buffer_id);
+ EXPECT_EQ(ANDROID_LOG_WARN, log_message->priority);
+ EXPECT_STREQ(LOG_TAG, log_message->tag);
+ EXPECT_EQ(expected_file, log_message->file);
+ EXPECT_EQ(expected_line, log_message->line);
+ EXPECT_EQ(expected_message, log_message->message);
+ };
+
+ __android_log_set_logger(liblog_logger_function);
+
+ expected_line = __LINE__ + 1;
+ LOG(WARNING) << expected_message;
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) {
+ using namespace android::base;
+ bool message_seen = false;
+ std::string expected_message = "libbase test message";
+
+ auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ message_seen = true;
+ EXPECT_EQ(MAIN, log_id);
+ EXPECT_EQ(WARNING, severity);
+ EXPECT_STREQ(LOG_TAG, tag);
+ EXPECT_EQ(nullptr, file);
+ EXPECT_EQ(0U, line);
+ EXPECT_EQ(expected_message, message);
+ };
+
+ SetLogger(LoggerFunction);
+
+ __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str());
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+}
+
+TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) {
+ using namespace android::base;
+ // These must be static since they're used by the liblog logger function, which only accepts
+ // lambdas without captures. The items used by the libbase logger are explicitly not static, to
+ // ensure that lambdas with captures do work there.
+ static bool message_seen = false;
+ static int expected_buffer_id = LOG_ID_MAIN;
+ static int expected_priority = ANDROID_LOG_WARN;
+ static std::string expected_message = "libbase test message";
+
+ auto liblog_logger_function = [](const struct __android_log_message* log_message) {
+ message_seen = true;
+ EXPECT_EQ(sizeof(__android_log_message), log_message->struct_size);
+ EXPECT_EQ(expected_buffer_id, log_message->buffer_id);
+ EXPECT_EQ(expected_priority, log_message->priority);
+ EXPECT_STREQ(LOG_TAG, log_message->tag);
+ EXPECT_STREQ(nullptr, log_message->file);
+ EXPECT_EQ(0U, log_message->line);
+ EXPECT_EQ(expected_message, log_message->message);
+ };
+
+ __android_log_set_logger(liblog_logger_function);
+
+ __android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str());
+ EXPECT_TRUE(message_seen);
+}
+
+TEST(liblog_global_state, SetAborter_with_liblog) {
+ using namespace android::base;
+
+ std::string expected_message = "libbase test message";
+ static bool message_seen = false;
+ auto aborter_function = [&](const char* message) {
+ message_seen = true;
+ EXPECT_EQ(expected_message, message);
+ };
+
+ SetAborter(aborter_function);
+ LOG(FATAL) << expected_message;
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+
+ static std::string expected_message_static = "libbase test message";
+ auto liblog_aborter_function = [](const char* message) {
+ message_seen = true;
+ EXPECT_EQ(expected_message_static, message);
+ };
+ __android_log_set_aborter(liblog_aborter_function);
+ LOG(FATAL) << expected_message_static;
+ EXPECT_TRUE(message_seen);
+ message_seen = false;
+}
+
+TEST(liblog_global_state, is_loggable_both_default) {
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+}
+
+TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
+#ifdef __ANDROID__
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+ android::base::SetProperty(log_tag_property, "d");
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ android::base::SetProperty(log_tag_property, "w");
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ android::base::SetProperty(log_tag_property, "");
+#else
+ GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
+
+TEST(liblog_global_state, is_loggable_both_set) {
+#ifdef __ANDROID__
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // When both a tag and a minimum priority are set, we use the lower value of the two.
+
+ // tag = warning, minimum_priority = debug, expect 'debug'
+ auto log_tag_property = std::string("log.tag.") + LOG_TAG;
+ android::base::SetProperty(log_tag_property, "w");
+ EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // tag = warning, minimum_priority = warning, expect 'warning'
+ EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // tag = debug, minimum_priority = warning, expect 'debug'
+ android::base::SetProperty(log_tag_property, "d");
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ // tag = debug, minimum_priority = debug, expect 'debug'
+ EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+
+ android::base::SetProperty(log_tag_property, "");
+#else
+ GTEST_SKIP() << "No log tag properties on host";
+#endif
+}
diff --git a/liblog/tests/liblog_host_test.cpp b/liblog/tests/liblog_host_test.cpp
new file mode 100644
index 000000000..ec186d4ac
--- /dev/null
+++ b/liblog/tests/liblog_host_test.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <private/android_logger.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <regex>
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+using android::base::InitLogging;
+using android::base::StderrLogger;
+using android::base::StringPrintf;
+
+static std::string MakeLogPattern(int priority, const char* tag, const char* message) {
+ static const char log_characters[] = "XXVDIWEF";
+ static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT,
+ "Mismatch in size of log_characters and values in android_LogPriority");
+ priority = priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : priority;
+ char log_char = log_characters[priority];
+
+ return StringPrintf("%s %c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s", tag, log_char,
+ message);
+}
+
+static void CheckMessage(bool expected, const std::string& output, int priority, const char* tag,
+ const char* message) {
+ std::regex message_regex(MakeLogPattern(priority, tag, message));
+ EXPECT_EQ(expected, std::regex_search(output, message_regex)) << message;
+}
+
+static void GenerateLogContent() {
+ __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_VERBOSE, "tag", "verbose main");
+ __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, "tag", "info main");
+ __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_ERROR, "tag", "error main");
+
+ __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+ __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, "tag", "info radio");
+ __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, "tag", "error radio");
+
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, "tag", "verbose system");
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, "tag", "info system");
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, "tag", "error system");
+
+ __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+ __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_INFO, "tag", "info crash");
+ __android_log_buf_print(LOG_ID_CRASH, ANDROID_LOG_ERROR, "tag", "error crash");
+}
+
+std::string GetPidString() {
+ int pid = getpid();
+ return StringPrintf("%5d", pid);
+}
+
+TEST(liblog, default_write) {
+ CapturedStderr captured_stderr;
+ InitLogging(nullptr, StderrLogger);
+
+ GenerateLogContent();
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
+}
+
+TEST(liblog, verbose_write) {
+ setenv("ANDROID_LOG_TAGS", "*:v", true);
+ CapturedStderr captured_stderr;
+ InitLogging(nullptr, StderrLogger);
+
+ GenerateLogContent();
+
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
+
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
+}
+
+TEST(liblog, error_write) {
+ setenv("ANDROID_LOG_TAGS", "*:e", true);
+ CapturedStderr captured_stderr;
+ InitLogging(nullptr, StderrLogger);
+
+ GenerateLogContent();
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose main");
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info main");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error main");
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose radio");
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info radio");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error radio");
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose system");
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info system");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error system");
+
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_VERBOSE, "tag", "verbose crash");
+ CheckMessage(false, captured_stderr.str(), ANDROID_LOG_INFO, "tag", "info crash");
+ CheckMessage(true, captured_stderr.str(), ANDROID_LOG_ERROR, "tag", "error crash");
+}
+
+TEST(liblog, kernel_no_write) {
+ CapturedStderr captured_stderr;
+ InitLogging(nullptr, StderrLogger);
+ __android_log_buf_print(LOG_ID_KERNEL, ANDROID_LOG_ERROR, "tag", "kernel error");
+ EXPECT_EQ("", captured_stderr.str());
+}
+
+TEST(liblog, binary_no_write) {
+ CapturedStderr captured_stderr;
+ InitLogging(nullptr, StderrLogger);
+ __android_log_buf_print(LOG_ID_EVENTS, ANDROID_LOG_ERROR, "tag", "error events");
+ __android_log_buf_print(LOG_ID_STATS, ANDROID_LOG_ERROR, "tag", "error stats");
+ __android_log_buf_print(LOG_ID_SECURITY, ANDROID_LOG_ERROR, "tag", "error security");
+
+ __android_log_bswrite(0x12, "events");
+ __android_log_stats_bwrite(0x34, "stats", strlen("stats"));
+ __android_log_security_bswrite(0x56, "security");
+
+ EXPECT_EQ("", captured_stderr.str());
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
new file mode 100644
index 000000000..a60d2df31
--- /dev/null
+++ b/liblog/tests/liblog_test.cpp
@@ -0,0 +1,2802 @@
+/*
+ * Copyright (C) 2013-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#ifdef __ANDROID__ // includes sys/properties.h which does not exist outside
+#include <cutils/properties.h>
+#endif
+#include <gtest/gtest.h>
+#include <log/log_event_list.h>
+#include <log/log_properties.h>
+#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+using android::base::make_scope_guard;
+
+// #define ENABLE_FLAKY_TESTS
+
+// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
+// non-syscall libs. Since we are only using this in the emergency of
+// a signal to stuff a terminating code into the logs, we will spin rather
+// than try a usleep.
+#define LOG_FAILURE_RETRY(exp) \
+ ({ \
+ typeof(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) || \
+ (_rc == -EINTR) || (_rc == -EAGAIN)); \
+ _rc; \
+ })
+
+// std::unique_ptr doesn't let you provide a pointer to a deleter (android_logger_list_close()) if
+// the type (struct logger_list) is an incomplete type, so we create ListCloser instead.
+struct ListCloser {
+ void operator()(struct logger_list* list) { android_logger_list_close(list); }
+};
+
+// This function is meant to be used for most log tests, it does the following:
+// 1) Open the log_buffer with a blocking reader
+// 2) Write the messages via write_messages
+// 3) Set an alarm for 2 seconds as a timeout
+// 4) Read until check_message returns true, which should be used to indicate the target message
+// is found
+// 5) Open log_buffer with a non_blocking reader and dump all messages
+// 6) Count the number of times check_messages returns true for these messages and assert it's
+// only 1.
+template <typename FWrite, typename FCheck>
+static void RunLogTests(log_id_t log_buffer, FWrite write_messages, FCheck check_message) {
+ pid_t pid = getpid();
+
+ auto logger_list = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY, 1000, pid)};
+ ASSERT_TRUE(logger_list);
+
+ write_messages();
+
+ alarm(2);
+ auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
+ bool found = false;
+ while (!found) {
+ log_msg log_msg;
+ ASSERT_GT(android_logger_list_read(logger_list.get(), &log_msg), 0);
+
+ ASSERT_EQ(log_buffer, log_msg.id());
+ ASSERT_EQ(pid, log_msg.entry.pid);
+
+ // TODO: Should this be an assert?
+ if (log_msg.msg() == nullptr) {
+ continue;
+ }
+
+ check_message(log_msg, &found);
+ }
+
+ auto logger_list_non_block = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)};
+ ASSERT_TRUE(logger_list_non_block);
+
+ size_t count = 0;
+ while (true) {
+ log_msg log_msg;
+ auto ret = android_logger_list_read(logger_list_non_block.get(), &log_msg);
+ if (ret == -EAGAIN) {
+ break;
+ }
+ ASSERT_GT(ret, 0);
+
+ ASSERT_EQ(log_buffer, log_msg.id());
+ ASSERT_EQ(pid, log_msg.entry.pid);
+
+ // TODO: Should this be an assert?
+ if (log_msg.msg() == nullptr) {
+ continue;
+ }
+
+ found = false;
+ check_message(log_msg, &found);
+ if (found) {
+ ++count;
+ }
+ }
+
+ EXPECT_EQ(1U, count);
+}
+
+TEST(liblog, __android_log_btwrite) {
+ int intBuf = 0xDEADBEEF;
+ EXPECT_LT(0,
+ __android_log_btwrite(0, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)));
+ long long longBuf = 0xDEADBEEFA55A5AA5;
+ EXPECT_LT(
+ 0, __android_log_btwrite(0, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)));
+ char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5";
+ EXPECT_LT(0,
+ __android_log_btwrite(0, EVENT_TYPE_STRING, Buf, sizeof(Buf) - 1));
+}
+
+#if defined(__ANDROID__)
+static std::string popenToString(const std::string& command) {
+ std::string ret;
+
+ FILE* fp = popen(command.c_str(), "re");
+ if (fp) {
+ if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
+ pclose(fp);
+ }
+ return ret;
+}
+
+#ifndef NO_PSTORE
+static bool isPmsgActive() {
+ pid_t pid = getpid();
+
+ std::string myPidFds =
+ popenToString(android::base::StringPrintf("ls -l /proc/%d/fd", pid));
+ if (myPidFds.length() == 0) return true; // guess it is?
+
+ return std::string::npos != myPidFds.find(" -> /dev/pmsg0");
+}
+#endif /* NO_PSTORE */
+
+static bool isLogdwActive() {
+ std::string logdwSignature =
+ popenToString("grep -a /dev/socket/logdw /proc/net/unix");
+ size_t beginning = logdwSignature.find(' ');
+ if (beginning == std::string::npos) return true;
+ beginning = logdwSignature.find(' ', beginning + 1);
+ if (beginning == std::string::npos) return true;
+ size_t end = logdwSignature.find(' ', beginning + 1);
+ if (end == std::string::npos) return true;
+ end = logdwSignature.find(' ', end + 1);
+ if (end == std::string::npos) return true;
+ end = logdwSignature.find(' ', end + 1);
+ if (end == std::string::npos) return true;
+ end = logdwSignature.find(' ', end + 1);
+ if (end == std::string::npos) return true;
+ std::string allLogdwEndpoints = popenToString(
+ "grep -a ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
+ " ' /proc/net/unix | " +
+ "sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
+ if (allLogdwEndpoints.length() == 0) return true;
+
+ // NB: allLogdwEndpoints has some false positives in it, but those
+ // strangers do not overlap with the simplistic activities inside this
+ // test suite.
+
+ pid_t pid = getpid();
+
+ std::string myPidFds =
+ popenToString(android::base::StringPrintf("ls -l /proc/%d/fd", pid));
+ if (myPidFds.length() == 0) return true;
+
+ // NB: fgrep with multiple strings is broken in Android
+ for (beginning = 0;
+ (end = allLogdwEndpoints.find('\n', beginning)) != std::string::npos;
+ beginning = end + 1) {
+ if (myPidFds.find(allLogdwEndpoints.substr(beginning, end - beginning)) !=
+ std::string::npos)
+ return true;
+ }
+ return false;
+}
+
+static bool tested__android_log_close;
+#endif
+
+TEST(liblog, __android_log_btwrite__android_logger_list_read) {
+#ifdef __ANDROID__
+ log_time ts(CLOCK_MONOTONIC);
+ log_time ts1(ts);
+
+ auto write_function = [&] {
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ // Check that we can close and reopen the logger
+ bool logdwActiveAfter__android_log_btwrite;
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
+#ifndef NO_PSTORE
+ bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+ logdwActiveAfter__android_log_btwrite = isLogdwActive();
+ EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+#endif /* NO_PSTORE */
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ }
+
+ ts1 = log_time(CLOCK_MONOTONIC);
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+ if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
+ bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+ logdwActiveAfter__android_log_btwrite = isLogdwActive();
+ EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+ }
+ };
+
+ int count = 0;
+ int second_count = 0;
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) {
+ return;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ return;
+ }
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts == tx) {
+ ++count;
+ } else if (ts1 == tx) {
+ ++second_count;
+ }
+
+ if (count == 1 && second_count == 1) {
+ count = 0;
+ second_count = 0;
+ *found = true;
+ }
+ };
+
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_write__android_logger_list_read) {
+#ifdef __ANDROID__
+ pid_t pid = getpid();
+
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld", pid, ts.tv_sec, ts.tv_nsec);
+ static const char tag[] = "liblog.__android_log_write__android_logger_list_read";
+ static const char prio = ANDROID_LOG_DEBUG;
+
+ std::string expected_message =
+ std::string(&prio, sizeof(prio)) + tag + std::string("", 1) + buf + std::string("", 1);
+
+ auto write_function = [&] { ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str())); };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if (log_msg.entry.len != expected_message.length()) {
+ return;
+ }
+
+ if (expected_message != std::string(log_msg.msg(), log_msg.entry.len)) {
+ return;
+ }
+
+ *found = true;
+ };
+
+ RunLogTests(LOG_ID_MAIN, write_function, check_function);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+static void bswrite_test(const char* message) {
+#ifdef __ANDROID__
+ pid_t pid = getpid();
+
+ log_time ts(android_log_clockid());
+
+ size_t num_lines = 1, size = 0, length = 0, total = 0;
+ const char* cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++cp;
+ ++total;
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 4 - 1 - 4) <= length) {
+ break;
+ }
+ }
+ while (*cp) {
+ ++cp;
+ ++total;
+ }
+
+ auto write_function = [&] { EXPECT_LT(0, __android_log_bswrite(0, message)); };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((size_t)log_msg.entry.len != (sizeof(android_log_event_string_t) + length) ||
+ log_msg.id() != LOG_ID_EVENTS) {
+ return;
+ }
+
+ android_log_event_string_t* eventData;
+ eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
+ return;
+ }
+
+ size_t len = eventData->length;
+ if (len == total) {
+ *found = true;
+
+ AndroidLogFormat* logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ if (length != total) {
+ fprintf(stderr, "Expect \"Binary log entry conversion failed\"\n");
+ }
+ int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+ &log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
+ if ((processBinaryLogBuffer == 0) || entry.message) {
+ size_t line_overhead = 20;
+ if (pid > 99999) ++line_overhead;
+ if (pid > 999999) ++line_overhead;
+ fflush(stderr);
+ if (processBinaryLogBuffer) {
+ EXPECT_GT((int)((line_overhead * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ } else {
+ EXPECT_EQ((int)((line_overhead * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ }
+ }
+ android_log_format_free(logformat);
+ }
+ };
+
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
+#else
+ message = NULL;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_bswrite_and_print) {
+ bswrite_test("Hello World");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__empty_string) {
+ bswrite_test("");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_prefix) {
+ bswrite_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_space_prefix) {
+ bswrite_test("\n Hello World \n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__multiple_newline) {
+ bswrite_test("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
+}
+
+static void buf_write_test(const char* message) {
+#ifdef __ANDROID__
+ pid_t pid = getpid();
+
+ static const char tag[] = "TEST__android_log_buf_write";
+ log_time ts(android_log_clockid());
+
+ auto write_function = [&] {
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
+ };
+ size_t num_lines = 1, size = 0, length = 0;
+ const char* cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 2 - sizeof(tag)) <= length) {
+ break;
+ }
+ ++cp;
+ }
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2) || log_msg.id() != LOG_ID_MAIN) {
+ return;
+ }
+
+ *found = true;
+
+ AndroidLogFormat* logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
+ size_t line_overhead = 11;
+ if (pid > 99999) ++line_overhead;
+ if (pid > 999999) ++line_overhead;
+ fflush(stderr);
+ EXPECT_EQ((int)(((line_overhead + sizeof(tag)) * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ }
+ android_log_format_free(logformat);
+ };
+
+ RunLogTests(LOG_ID_MAIN, write_function, check_function);
+
+#else
+ message = NULL;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_buf_write_and_print__empty) {
+ buf_write_test("");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_prefix) {
+ buf_write_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_space_prefix) {
+ buf_write_test("\n Hello World \n");
+}
+
+#ifdef ENABLE_FLAKY_TESTS
+#ifdef __ANDROID__
+static unsigned signaled;
+static log_time signal_time;
+
+/*
+ * Strictly, we are not allowed to log messages in a signal context, but we
+ * do make an effort to keep the failure surface minimized, and this in-effect
+ * should catch any regressions in that effort. The odds of a logged message
+ * in a signal handler causing a lockup problem should be _very_ small.
+ */
+static void caught_blocking_signal(int /*signum*/) {
+ unsigned long long v = 0xDEADBEEFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ ++signaled;
+ if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+ signal_time = log_time(CLOCK_MONOTONIC);
+ signal_time.tv_sec += 2;
+ }
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+// Fill in current process user and system time in 10ms increments
+static void get_ticks(unsigned long long* uticks, unsigned long long* sticks) {
+ *uticks = *sticks = 0;
+
+ pid_t pid = getpid();
+
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid);
+
+ FILE* fp = fopen(buffer, "re");
+ if (!fp) {
+ return;
+ }
+
+ char* cp = fgets(buffer, sizeof(buffer), fp);
+ fclose(fp);
+ if (!cp) {
+ return;
+ }
+
+ pid_t d;
+ char s[sizeof(buffer)];
+ char c;
+ long long ll;
+ unsigned long long ull;
+
+ if (15 != sscanf(buffer,
+ "%d %s %c %lld %lld %lld %lld %lld %llu %llu %llu %llu %llu "
+ "%llu %llu ",
+ &d, s, &c, &ll, &ll, &ll, &ll, &ll, &ull, &ull, &ull, &ull,
+ &ull, uticks, sticks)) {
+ *uticks = *sticks = 0;
+ }
+}
+#endif
+
+TEST(liblog, android_logger_list_read__cpu_signal) {
+#ifdef __ANDROID__
+ struct logger_list* logger_list;
+ unsigned long long v = 0xDEADBEEFA55A0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+ int count = 0;
+
+ int signals = 0;
+
+ unsigned long long uticks_start;
+ unsigned long long sticks_start;
+ get_ticks(&uticks_start, &sticks_start);
+
+ const unsigned alarm_time = 10;
+
+ memset(&signal_time, 0, sizeof(signal_time));
+
+ signal(SIGALRM, caught_blocking_signal);
+ alarm(alarm_time);
+
+ signaled = 0;
+
+ do {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ alarm(alarm_time);
+
+ ++count;
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+ unsigned long long l = cp[0] & 0xFF;
+ l |= (unsigned long long)(cp[1] & 0xFF) << 8;
+ l |= (unsigned long long)(cp[2] & 0xFF) << 16;
+ l |= (unsigned long long)(cp[3] & 0xFF) << 24;
+ l |= (unsigned long long)(cp[4] & 0xFF) << 32;
+ l |= (unsigned long long)(cp[5] & 0xFF) << 40;
+ l |= (unsigned long long)(cp[6] & 0xFF) << 48;
+ l |= (unsigned long long)(cp[7] & 0xFF) << 56;
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ EXPECT_LE(1, count);
+
+ EXPECT_EQ(1, signals);
+
+ android_logger_list_close(logger_list);
+
+ unsigned long long uticks_end;
+ unsigned long long sticks_end;
+ get_ticks(&uticks_end, &sticks_end);
+
+ // Less than 1% in either user or system time, or both
+ const unsigned long long one_percent_ticks = alarm_time;
+ unsigned long long user_ticks = uticks_end - uticks_start;
+ unsigned long long system_ticks = sticks_end - sticks_start;
+ EXPECT_GT(one_percent_ticks, user_ticks);
+ EXPECT_GT(one_percent_ticks, system_ticks);
+ EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+/*
+ * Strictly, we are not allowed to log messages in a signal context, the
+ * correct way to handle this is to ensure the messages are constructed in
+ * a thread; the signal handler should only unblock the thread.
+ */
+static sem_t thread_trigger;
+
+static void caught_blocking_thread(int /*signum*/) {
+ sem_post(&thread_trigger);
+}
+
+static void* running_thread(void*) {
+ unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ struct timespec timeout;
+ clock_gettime(CLOCK_REALTIME, &timeout);
+ timeout.tv_sec += 55;
+ sem_timedwait(&thread_trigger, &timeout);
+
+ ++signaled;
+ if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+ signal_time = log_time(CLOCK_MONOTONIC);
+ signal_time.tv_sec += 2;
+ }
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ return NULL;
+}
+
+static int start_thread() {
+ sem_init(&thread_trigger, 0, 0);
+
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr)) {
+ return -1;
+ }
+
+ struct sched_param param;
+
+ memset(&param, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, &param);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ pthread_attr_destroy(&attr);
+ return -1;
+ }
+
+ pthread_t thread;
+ if (pthread_create(&thread, &attr, running_thread, NULL)) {
+ pthread_attr_destroy(&attr);
+ return -1;
+ }
+
+ pthread_attr_destroy(&attr);
+ return 0;
+}
+#endif
+
+TEST(liblog, android_logger_list_read__cpu_thread) {
+#ifdef __ANDROID__
+ struct logger_list* logger_list;
+ unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+ int count = 0;
+
+ int signals = 0;
+
+ unsigned long long uticks_start;
+ unsigned long long sticks_start;
+ get_ticks(&uticks_start, &sticks_start);
+
+ const unsigned alarm_time = 10;
+
+ memset(&signal_time, 0, sizeof(signal_time));
+
+ signaled = 0;
+ EXPECT_EQ(0, start_thread());
+
+ signal(SIGALRM, caught_blocking_thread);
+ alarm(alarm_time);
+
+ do {
+ log_msg log_msg;
+ if (LOG_FAILURE_RETRY(android_logger_list_read(logger_list, &log_msg)) <= 0) {
+ break;
+ }
+
+ alarm(alarm_time);
+
+ ++count;
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
+ (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+ unsigned long long l = cp[0] & 0xFF;
+ l |= (unsigned long long)(cp[1] & 0xFF) << 8;
+ l |= (unsigned long long)(cp[2] & 0xFF) << 16;
+ l |= (unsigned long long)(cp[3] & 0xFF) << 24;
+ l |= (unsigned long long)(cp[4] & 0xFF) << 32;
+ l |= (unsigned long long)(cp[5] & 0xFF) << 40;
+ l |= (unsigned long long)(cp[6] & 0xFF) << 48;
+ l |= (unsigned long long)(cp[7] & 0xFF) << 56;
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ EXPECT_LE(1, count);
+
+ EXPECT_EQ(1, signals);
+
+ android_logger_list_close(logger_list);
+
+ unsigned long long uticks_end;
+ unsigned long long sticks_end;
+ get_ticks(&uticks_end, &sticks_end);
+
+ // Less than 1% in either user or system time, or both
+ const unsigned long long one_percent_ticks = alarm_time;
+ unsigned long long user_ticks = uticks_end - uticks_start;
+ unsigned long long system_ticks = sticks_end - sticks_start;
+ EXPECT_GT(one_percent_ticks, user_ticks);
+ EXPECT_GT(one_percent_ticks, system_ticks);
+ EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // ENABLE_FLAKY_TESTS
+
+static const char max_payload_buf[] =
+ "LEONATO\n\
+I learn in this letter that Don Peter of Arragon\n\
+comes this night to Messina\n\
+MESSENGER\n\
+He is very near by this: he was not three leagues off\n\
+when I left him\n\
+LEONATO\n\
+How many gentlemen have you lost in this action?\n\
+MESSENGER\n\
+But few of any sort, and none of name\n\
+LEONATO\n\
+A victory is twice itself when the achiever brings\n\
+home full numbers. I find here that Don Peter hath\n\
+bestowed much honour on a young Florentine called Claudio\n\
+MESSENGER\n\
+Much deserved on his part and equally remembered by\n\
+Don Pedro: he hath borne himself beyond the\n\
+promise of his age, doing, in the figure of a lamb,\n\
+the feats of a lion: he hath indeed better\n\
+bettered expectation than you must expect of me to\n\
+tell you how\n\
+LEONATO\n\
+He hath an uncle here in Messina will be very much\n\
+glad of it.\n\
+MESSENGER\n\
+I have already delivered him letters, and there\n\
+appears much joy in him; even so much that joy could\n\
+not show itself modest enough without a badge of\n\
+bitterness.\n\
+LEONATO\n\
+Did he break out into tears?\n\
+MESSENGER\n\
+In great measure.\n\
+LEONATO\n\
+A kind overflow of kindness: there are no faces\n\
+truer than those that are so washed. How much\n\
+better is it to weep at joy than to joy at weeping!\n\
+BEATRICE\n\
+I pray you, is Signior Mountanto returned from the\n\
+wars or no?\n\
+MESSENGER\n\
+I know none of that name, lady: there was none such\n\
+in the army of any sort.\n\
+LEONATO\n\
+What is he that you ask for, niece?\n\
+HERO\n\
+My cousin means Signior Benedick of Padua.\n\
+MESSENGER\n\
+O, he's returned; and as pleasant as ever he was.\n\
+BEATRICE\n\
+He set up his bills here in Messina and challenged\n\
+Cupid at the flight; and my uncle's fool, reading\n\
+the challenge, subscribed for Cupid, and challenged\n\
+him at the bird-bolt. I pray you, how many hath he\n\
+killed and eaten in these wars? But how many hath\n\
+he killed? for indeed I promised to eat all of his killing.\n\
+LEONATO\n\
+Faith, niece, you tax Signior Benedick too much;\n\
+but he'll be meet with you, I doubt it not.\n\
+MESSENGER\n\
+He hath done good service, lady, in these wars.\n\
+BEATRICE\n\
+You had musty victual, and he hath holp to eat it:\n\
+he is a very valiant trencherman; he hath an\n\
+excellent stomach.\n\
+MESSENGER\n\
+And a good soldier too, lady.\n\
+BEATRICE\n\
+And a good soldier to a lady: but what is he to a lord?\n\
+MESSENGER\n\
+A lord to a lord, a man to a man; stuffed with all\n\
+honourable virtues.\n\
+BEATRICE\n\
+It is so, indeed; he is no less than a stuffed man:\n\
+but for the stuffing,--well, we are all mortal.\n\
+LEONATO\n\
+You must not, sir, mistake my niece. There is a\n\
+kind of merry war betwixt Signior Benedick and her:\n\
+they never meet but there's a skirmish of wit\n\
+between them.\n\
+BEATRICE\n\
+Alas! he gets nothing by that. In our last\n\
+conflict four of his five wits went halting off, and\n\
+now is the whole man governed with one: so that if\n\
+he have wit enough to keep himself warm, let him\n\
+bear it for a difference between himself and his\n\
+horse; for it is all the wealth that he hath left,\n\
+to be known a reasonable creature. Who is his\n\
+companion now? He hath every month a new sworn brother.\n\
+MESSENGER\n\
+Is't possible?\n\
+BEATRICE\n\
+Very easily possible: he wears his faith but as\n\
+the fashion of his hat; it ever changes with the\n\
+next block.\n\
+MESSENGER\n\
+I see, lady, the gentleman is not in your books.\n\
+BEATRICE\n\
+No; an he were, I would burn my study. But, I pray\n\
+you, who is his companion? Is there no young\n\
+squarer now that will make a voyage with him to the devil?\n\
+MESSENGER\n\
+He is most in the company of the right noble Claudio.\n\
+BEATRICE\n\
+O Lord, he will hang upon him like a disease: he\n\
+is sooner caught than the pestilence, and the taker\n\
+runs presently mad. God help the noble Claudio! if\n\
+he have caught the Benedick, it will cost him a\n\
+thousand pound ere a' be cured.\n\
+MESSENGER\n\
+I will hold friends with you, lady.\n\
+BEATRICE\n\
+Do, good friend.\n\
+LEONATO\n\
+You will never run mad, niece.\n\
+BEATRICE\n\
+No, not till a hot January.\n\
+MESSENGER\n\
+Don Pedro is approached.\n\
+Enter DON PEDRO, DON JOHN, CLAUDIO, BENEDICK, and BALTHASAR\n\
+\n\
+DON PEDRO\n\
+Good Signior Leonato, you are come to meet your\n\
+trouble: the fashion of the world is to avoid\n\
+cost, and you encounter it\n\
+LEONATO\n\
+Never came trouble to my house in the likeness of your grace,\n\
+for trouble being gone, comfort should remain, but\n\
+when you depart from me, sorrow abides and happiness\n\
+takes his leave.";
+
+TEST(liblog, max_payload) {
+#ifdef __ANDROID__
+ static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
+#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(max_payload_tag) - 1)
+
+ pid_t pid = getpid();
+ char tag[sizeof(max_payload_tag)];
+ memcpy(tag, max_payload_tag, sizeof(tag));
+ snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+ auto write_function = [&] {
+ LOG_FAILURE_RETRY(
+ __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, max_payload_buf));
+ };
+
+ ssize_t max_len = 0;
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ char* data = log_msg.msg();
+
+ if (!data || strcmp(++data, tag)) {
+ return;
+ }
+
+ data += strlen(data) + 1;
+
+ const char* left = data;
+ const char* right = max_payload_buf;
+ while (*left && *right && (*left == *right)) {
+ ++left;
+ ++right;
+ }
+
+ if (max_len <= (left - data)) {
+ max_len = left - data + 1;
+ }
+
+ if (max_len > 512) {
+ *found = true;
+ }
+ };
+
+ RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
+
+ EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_buf_print__maxtag) {
+#ifdef __ANDROID__
+ auto write_function = [&] {
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, max_payload_buf,
+ max_payload_buf));
+ };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD) {
+ return;
+ }
+
+ *found = true;
+
+ AndroidLogFormat* logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
+ fflush(stderr);
+ int printLogLine =
+ android_log_printLogLine(logformat, fileno(stderr), &entry);
+ // Legacy tag truncation
+ EXPECT_LE(128, printLogLine);
+ // Measured maximum if we try to print part of the tag as message
+ EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
+ }
+ android_log_format_free(logformat);
+ };
+
+ RunLogTests(LOG_ID_MAIN, write_function, check_function);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+// TODO: This test is tautological. android_logger_list_read() calls recv() with
+// LOGGER_ENTRY_MAX_PAYLOAD as its size argument, so it's not possible for this test to read a
+// payload larger than that size.
+TEST(liblog, too_big_payload) {
+#ifdef __ANDROID__
+ pid_t pid = getpid();
+ static const char big_payload_tag[] = "TEST_big_payload_XXXX";
+ char tag[sizeof(big_payload_tag)];
+ memcpy(tag, big_payload_tag, sizeof(tag));
+ snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+ std::string longString(3266519, 'x');
+ ssize_t ret;
+
+ auto write_function = [&] {
+ ret = LOG_FAILURE_RETRY(
+ __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
+ };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ char* data = log_msg.msg();
+
+ if (!data || strcmp(++data, tag)) {
+ return;
+ }
+
+ data += strlen(data) + 1;
+
+ const char* left = data;
+ const char* right = longString.c_str();
+ while (*left && *right && (*left == *right)) {
+ ++left;
+ ++right;
+ }
+
+ ssize_t len = left - data + 1;
+ // Check that we don't see any entries larger than the max payload.
+ EXPECT_LE(static_cast<size_t>(len), LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag));
+
+ // Once we've found our expected entry, break.
+ if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) {
+ *found = true;
+ }
+ };
+
+ RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, dual_reader) {
+#ifdef __ANDROID__
+ static const int expected_count1 = 25;
+ static const int expected_count2 = 25;
+
+ pid_t pid = getpid();
+
+ auto logger_list1 = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count1, pid)};
+ ASSERT_TRUE(logger_list1);
+
+ auto logger_list2 = std::unique_ptr<struct logger_list, ListCloser>{
+ android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count2, pid)};
+ ASSERT_TRUE(logger_list2);
+
+ for (int i = 25; i > 0; --i) {
+ static const char fmt[] = "dual_reader %02d";
+ char buffer[sizeof(fmt) + 8];
+ snprintf(buffer, sizeof(buffer), fmt, i);
+ LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "liblog", buffer));
+ }
+
+ alarm(2);
+ auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
+
+ // Wait until we see all messages with the blocking reader.
+ int count1 = 0;
+ int count2 = 0;
+
+ while (count1 != expected_count2 || count2 != expected_count2) {
+ log_msg log_msg;
+ if (count1 < expected_count1) {
+ ASSERT_GT(android_logger_list_read(logger_list1.get(), &log_msg), 0);
+ count1++;
+ }
+ if (count2 < expected_count2) {
+ ASSERT_GT(android_logger_list_read(logger_list2.get(), &log_msg), 0);
+ count2++;
+ }
+ }
+
+ // Test again with the nonblocking reader.
+ auto logger_list_non_block1 =
+ std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count1, pid)};
+ ASSERT_TRUE(logger_list_non_block1);
+
+ auto logger_list_non_block2 =
+ std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count2, pid)};
+ ASSERT_TRUE(logger_list_non_block2);
+ count1 = 0;
+ count2 = 0;
+ bool done1 = false;
+ bool done2 = false;
+
+ while (!done1 || !done2) {
+ log_msg log_msg;
+
+ if (!done1) {
+ if (android_logger_list_read(logger_list_non_block1.get(), &log_msg) <= 0) {
+ done1 = true;
+ } else {
+ ++count1;
+ }
+ }
+
+ if (!done2) {
+ if (android_logger_list_read(logger_list_non_block2.get(), &log_msg) <= 0) {
+ done2 = true;
+ } else {
+ ++count2;
+ }
+ }
+ }
+
+ EXPECT_EQ(expected_count1, count1);
+ EXPECT_EQ(expected_count2, count2);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+static bool checkPriForTag(AndroidLogFormat* p_format, const char* tag,
+ android_LogPriority pri) {
+ return android_log_shouldPrintLine(p_format, tag, pri) &&
+ !android_log_shouldPrintLine(p_format, tag,
+ (android_LogPriority)(pri - 1));
+}
+
+TEST(liblog, filterRule) {
+ static const char tag[] = "random";
+
+ AndroidLogFormat* p_format = android_log_format_new();
+
+ android_log_addFilterRule(p_format, "*:i");
+
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_INFO));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) ==
+ 0);
+ android_log_addFilterRule(p_format, "*");
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "*:v");
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "*:i");
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_INFO));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) ==
+ 0);
+
+ android_log_addFilterRule(p_format, tag);
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:v");
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:d");
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+ android_log_addFilterRule(p_format, "random:w");
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_WARN));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) ==
+ 0);
+
+ android_log_addFilterRule(p_format, "crap:*");
+ EXPECT_TRUE(checkPriForTag(p_format, "crap", ANDROID_LOG_VERBOSE));
+ EXPECT_TRUE(
+ android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0);
+
+ // invalid expression
+ EXPECT_TRUE(android_log_addFilterRule(p_format, "random:z") < 0);
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_WARN));
+ EXPECT_TRUE(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) ==
+ 0);
+
+ // Issue #550946
+ EXPECT_TRUE(android_log_addFilterString(p_format, " ") == 0);
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_WARN));
+
+ // note trailing space
+ EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:d ") == 0);
+ EXPECT_TRUE(checkPriForTag(p_format, tag, ANDROID_LOG_DEBUG));
+
+ EXPECT_TRUE(android_log_addFilterString(p_format, "*:s random:z") < 0);
+
+#if 0 // bitrot, seek update
+ char defaultBuffer[512];
+
+ android_log_formatLogLine(p_format,
+ defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123,
+ 123, 123, tag, "nofile", strlen("Hello"), "Hello", NULL);
+
+ fprintf(stderr, "%s\n", defaultBuffer);
+#endif
+
+ android_log_format_free(p_format);
+}
+
+#ifdef ENABLE_FLAKY_TESTS
+TEST(liblog, is_loggable) {
+#ifdef __ANDROID__
+ static const char tag[] = "is_loggable";
+ static const char log_namespace[] = "persist.log.tag.";
+ static const size_t base_offset = 8; /* skip "persist." */
+ // sizeof("string") = strlen("string") + 1
+ char key[sizeof(log_namespace) + sizeof(tag) - 1];
+ char hold[4][PROP_VALUE_MAX];
+ static const struct {
+ int level;
+ char type;
+ } levels[] = {
+ {ANDROID_LOG_VERBOSE, 'v'}, {ANDROID_LOG_DEBUG, 'd'},
+ {ANDROID_LOG_INFO, 'i'}, {ANDROID_LOG_WARN, 'w'},
+ {ANDROID_LOG_ERROR, 'e'}, {ANDROID_LOG_FATAL, 'a'},
+ {ANDROID_LOG_SILENT, 's'}, {-2, 'g'}, // Illegal value, resort to default
+ };
+
+ // Set up initial test condition
+ memset(hold, 0, sizeof(hold));
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ property_get(key, hold[0], "");
+ property_set(key, "");
+ property_get(key + base_offset, hold[1], "");
+ property_set(key + base_offset, "");
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ property_get(key, hold[2], "");
+ property_set(key, "");
+ property_get(key, hold[3], "");
+ property_set(key + base_offset, "");
+
+ // All combinations of level and defaults
+ for (size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+ if (levels[i].level == -2) {
+ continue;
+ }
+ for (size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+ if (levels[j].level == -2) {
+ continue;
+ }
+ fprintf(stderr, "i=%zu j=%zu\r", i, j);
+ bool android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), levels[j].level);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1)) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), levels[j].level));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), levels[j].level));
+ }
+ }
+ }
+ }
+
+ // All combinations of level and tag and global properties
+ for (size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+ if (levels[i].level == -2) {
+ continue;
+ }
+ for (size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+ char buf[2];
+ buf[0] = levels[j].type;
+ buf[1] = '\0';
+
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", i, j, key,
+ buf);
+ usleep(20000);
+ property_set(key, buf);
+ bool android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1) ||
+ ((levels[i].level < ANDROID_LOG_DEBUG) && (levels[j].level == -2))) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ }
+ usleep(20000);
+ property_set(key, "");
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", i, j,
+ key + base_offset, buf);
+ property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1) ||
+ ((levels[i].level < ANDROID_LOG_DEBUG) && (levels[j].level == -2))) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ }
+ usleep(20000);
+ property_set(key + base_offset, "");
+
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", i, j, key,
+ buf);
+ property_set(key, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1) ||
+ ((levels[i].level < ANDROID_LOG_DEBUG) && (levels[j].level == -2))) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ }
+ usleep(20000);
+ property_set(key, "");
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", i, j,
+ key + base_offset, buf);
+ property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1) ||
+ ((levels[i].level < ANDROID_LOG_DEBUG) && (levels[j].level == -2))) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ }
+ usleep(20000);
+ property_set(key + base_offset, "");
+ }
+ }
+
+ // All combinations of level and tag properties, but with global set to INFO
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ usleep(20000);
+ property_set(key, "I");
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ for (size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+ if (levels[i].level == -2) {
+ continue;
+ }
+ for (size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+ char buf[2];
+ buf[0] = levels[j].type;
+ buf[1] = '\0';
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", i, j, key,
+ buf);
+ usleep(20000);
+ property_set(key, buf);
+ bool android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1) ||
+ ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
+ && (levels[j].level == -2))) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ }
+ usleep(20000);
+ property_set(key, "");
+
+ fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r", i, j,
+ key + base_offset, buf);
+ property_set(key + base_offset, buf);
+ android_log_is_loggable = __android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG);
+ if ((levels[i].level < levels[j].level) || (levels[j].level == -1) ||
+ ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
+ && (levels[j].level == -2))) {
+ if (android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_FALSE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_FALSE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ } else {
+ if (!android_log_is_loggable) {
+ fprintf(stderr, "\n");
+ }
+ EXPECT_TRUE(android_log_is_loggable);
+ for (size_t k = 10; k; --k) {
+ EXPECT_TRUE(__android_log_is_loggable_len(
+ levels[i].level, tag, strlen(tag), ANDROID_LOG_DEBUG));
+ }
+ }
+ usleep(20000);
+ property_set(key + base_offset, "");
+ }
+ }
+
+ // reset parms
+ snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+ usleep(20000);
+ property_set(key, hold[0]);
+ property_set(key + base_offset, hold[1]);
+ strcpy(key, log_namespace);
+ key[sizeof(log_namespace) - 2] = '\0';
+ property_set(key, hold[2]);
+ property_set(key + base_offset, hold[3]);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // ENABLE_FLAKY_TESTS
+
+#ifdef ENABLE_FLAKY_TESTS
+// Following tests the specific issues surrounding error handling wrt logd.
+// Kills logd and toss all collected data, equivalent to logcat -b all -c,
+// except we also return errors to the logging callers.
+#ifdef __ANDROID__
+// helper to liblog.enoent to count end-to-end matching logging messages.
+static int count_matching_ts(log_time ts) {
+ usleep(1000000);
+
+ pid_t pid = getpid();
+
+ struct logger_list* logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid);
+
+ int count = 0;
+ if (logger_list == NULL) return count;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ if (log_msg.entry.len != sizeof(android_log_event_long_t)) continue;
+ if (log_msg.id() != LOG_ID_EVENTS) continue;
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+ if (!eventData) continue;
+ if (eventData->payload.type != EVENT_TYPE_LONG) continue;
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts != tx) continue;
+
+ // found event message with matching timestamp signature in payload
+ ++count;
+ }
+ android_logger_list_close(logger_list);
+
+ return count;
+}
+
+TEST(liblog, enoent) {
+#ifdef __ANDROID__
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
+ log_time ts(CLOCK_MONOTONIC);
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ EXPECT_EQ(1, count_matching_ts(ts));
+
+ // This call will fail unless we are root, beware of any
+ // test prior to this one playing with setuid and causing interference.
+ // We need to run before these tests so that they do not interfere with
+ // this test.
+ //
+ // Stopping the logger can affect some other test's expectations as they
+ // count on the log buffers filled with existing content, and this
+ // effectively does a logcat -c emptying it. So we want this test to be
+ // as near as possible to the bottom of the file. For example
+ // liblog.android_logger_get_ is one of those tests that has no recourse
+ // and that would be adversely affected by emptying the log if it was run
+ // right after this test.
+ system("stop logd");
+ usleep(1000000);
+
+ // A clean stop like we are testing returns -ENOENT, but in the _real_
+ // world we could get -ENOTCONN or -ECONNREFUSED depending on timing.
+ // Alas we can not test these other return values; accept that they
+ // are treated equally within the open-retry logic in liblog.
+ ts = log_time(CLOCK_MONOTONIC);
+ int ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
+ std::string content = android::base::StringPrintf(
+ "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
+ ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
+ EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
+ ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
+ content = android::base::StringPrintf(
+ "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
+ ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
+ EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
+ EXPECT_EQ(0, count_matching_ts(ts));
+
+ system("start logd");
+ usleep(1000000);
+
+ EXPECT_EQ(0, count_matching_ts(ts));
+
+ ts = log_time(CLOCK_MONOTONIC);
+ EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ EXPECT_EQ(1, count_matching_ts(ts));
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // __ANDROID__
+#endif // ENABLE_FLAKY_TESTS
+
+// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
+
+#ifdef ENABLE_FLAKY_TESTS
+// Do not retest properties, and cannot log into LOG_ID_SECURITY
+TEST(liblog, __security) {
+#ifdef __ANDROID__
+ static const char persist_key[] = "persist.logd.security";
+ static const char readonly_key[] = "ro.organization_owned";
+ // A silly default value that can never be in readonly_key so
+ // that it can be determined the property is not set.
+ static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
+ char persist[PROP_VALUE_MAX];
+ char persist_hold[PROP_VALUE_MAX];
+ char readonly[PROP_VALUE_MAX];
+
+ // First part of this test requires the test itself to have the appropriate
+ // permissions. If we do not have them, we can not override them, so we
+ // bail rather than give a failing grade.
+ property_get(persist_key, persist, "");
+ fprintf(stderr, "INFO: getprop %s -> %s\n", persist_key, persist);
+ strncpy(persist_hold, persist, PROP_VALUE_MAX);
+ property_get(readonly_key, readonly, nothing_val);
+ fprintf(stderr, "INFO: getprop %s -> %s\n", readonly_key, readonly);
+
+ if (!strcmp(readonly, nothing_val)) {
+ // Lets check if we can set the value (we should not be allowed to do so)
+ EXPECT_FALSE(__android_log_security());
+ fprintf(stderr, "WARNING: setting ro.organization_owned to a domain\n");
+ static const char domain[] = "com.google.android.SecOps.DeviceOwner";
+ EXPECT_NE(0, property_set(readonly_key, domain));
+ useconds_t total_time = 0;
+ static const useconds_t seconds = 1000000;
+ static const useconds_t max_time = 5 * seconds; // not going to happen
+ static const useconds_t rest = 20 * 1000;
+ for (; total_time < max_time; total_time += rest) {
+ usleep(rest); // property system does not guarantee performance.
+ property_get(readonly_key, readonly, nothing_val);
+ if (!strcmp(readonly, domain)) {
+ if (total_time > rest) {
+ fprintf(stderr, "INFO: took %u.%06u seconds to set property\n",
+ (unsigned)(total_time / seconds),
+ (unsigned)(total_time % seconds));
+ }
+ break;
+ }
+ }
+ EXPECT_STRNE(domain, readonly);
+ }
+
+ if (!strcasecmp(readonly, "false") || !readonly[0] ||
+ !strcmp(readonly, nothing_val)) {
+ // not enough permissions to run tests surrounding persist.logd.security
+ EXPECT_FALSE(__android_log_security());
+ return;
+ }
+
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "TRUE");
+ property_get(persist_key, persist, "");
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ bool perm = (gid == AID_ROOT) || (uid == AID_ROOT);
+ EXPECT_STREQ(perm ? "TRUE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "FALSE");
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "FALSE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "true");
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "true" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "false");
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "false" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, "");
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, persist_hold);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(persist_hold, persist);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __security_buffer) {
+#ifdef __ANDROID__
+ struct logger_list* logger_list;
+ android_event_long_t buffer;
+
+ static const char persist_key[] = "persist.logd.security";
+ char persist[PROP_VALUE_MAX];
+ bool set_persist = false;
+ bool allow_security = false;
+
+ if (__android_log_security()) {
+ allow_security = true;
+ } else {
+ property_get(persist_key, persist, "");
+ if (strcasecmp(persist, "true")) {
+ property_set(persist_key, "TRUE");
+ if (__android_log_security()) {
+ allow_security = true;
+ set_persist = true;
+ } else {
+ property_set(persist_key, persist);
+ }
+ }
+ }
+
+ if (!allow_security) {
+ fprintf(stderr,
+ "WARNING: "
+ "security buffer disabled, bypassing end-to-end test\n");
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t*>((void*)&ts));
+
+ // expect failure!
+ ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+ return;
+ }
+
+ /* Matches clientHasLogCredentials() in logd */
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ bool clientHasLogCredentials = true;
+ if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG) &&
+ (gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
+ uid_t euid = geteuid();
+ if ((euid != AID_SYSTEM) && (euid != AID_ROOT) && (euid != AID_LOG)) {
+ gid_t egid = getegid();
+ if ((egid != AID_SYSTEM) && (egid != AID_ROOT) && (egid != AID_LOG)) {
+ int num_groups = getgroups(0, NULL);
+ if (num_groups > 0) {
+ gid_t groups[num_groups];
+ num_groups = getgroups(num_groups, groups);
+ while (num_groups > 0) {
+ if (groups[num_groups - 1] == AID_LOG) {
+ break;
+ }
+ --num_groups;
+ }
+ }
+ if (num_groups <= 0) {
+ clientHasLogCredentials = false;
+ }
+ }
+ }
+ }
+ if (!clientHasLogCredentials) {
+ fprintf(stderr,
+ "WARNING: "
+ "not in system context, bypassing end-to-end test\n");
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t*>((void*)&ts));
+
+ // expect failure!
+ ASSERT_GE(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+
+ return;
+ }
+
+ EXPECT_EQ(0, setuid(AID_SYSTEM)); // only one that can read security buffer
+
+ uid = getuid();
+ gid = getgid();
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL !=
+ (logger_list = android_logger_list_open(
+ LOG_ID_SECURITY, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ 1000, pid)));
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ buffer.type = EVENT_TYPE_LONG;
+ buffer.data = *(static_cast<uint64_t*>((void*)&ts));
+
+ ASSERT_LT(0, __android_log_security_bwrite(0, &buffer, sizeof(buffer)));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
+ (log_msg.id() != LOG_ID_SECURITY)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ if (set_persist) {
+ property_set(persist_key, persist);
+ }
+
+ android_logger_list_close(logger_list);
+
+ bool clientHasSecurityCredentials = (uid == AID_SYSTEM) || (gid == AID_SYSTEM);
+ if (!clientHasSecurityCredentials) {
+ fprintf(stderr,
+ "WARNING: "
+ "not system, content submitted but can not check end-to-end\n");
+ }
+ EXPECT_EQ(clientHasSecurityCredentials ? 1 : 0, count);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // ENABLE_FLAKY_TESTS
+
+#ifdef __ANDROID__
+static void android_errorWriteWithInfoLog_helper(int tag, const char* subtag, int uid,
+ const char* payload, int data_len) {
+ auto write_function = [&] {
+ int ret = android_errorWriteWithInfoLog(tag, subtag, uid, payload, data_len);
+ ASSERT_LT(0, ret);
+ };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ char* event_data = log_msg.msg();
+ char* original = event_data;
+
+ // Tag
+ auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
+ event_data += sizeof(android_event_header_t);
+ if (event_header->tag != tag) {
+ return;
+ }
+
+ // List type
+ auto* event_list = reinterpret_cast<android_event_list_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_LIST, event_list->type);
+ ASSERT_EQ(3, event_list->element_count);
+ event_data += sizeof(android_event_list_t);
+
+ // Element #1: string type for subtag
+ auto* event_string_subtag = reinterpret_cast<android_event_string_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_STRING, event_string_subtag->type);
+ int32_t subtag_len = strlen(subtag);
+ if (subtag_len > 32) {
+ subtag_len = 32;
+ }
+ ASSERT_EQ(subtag_len, event_string_subtag->length);
+ if (memcmp(subtag, &event_string_subtag->data, subtag_len)) {
+ return;
+ }
+ event_data += sizeof(android_event_string_t) + subtag_len;
+
+ // Element #2: int type for uid
+ auto* event_int_uid = reinterpret_cast<android_event_int_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_INT, event_int_uid->type);
+ ASSERT_EQ(uid, event_int_uid->data);
+ event_data += sizeof(android_event_int_t);
+
+ // Element #3: string type for data
+ auto* event_string_data = reinterpret_cast<android_event_string_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_STRING, event_string_data->type);
+ int32_t message_data_len = event_string_data->length;
+ if (data_len < 512) {
+ ASSERT_EQ(data_len, message_data_len);
+ }
+ if (memcmp(payload, &event_string_data->data, message_data_len) != 0) {
+ return;
+ }
+ event_data += sizeof(android_event_string_t);
+
+ if (data_len >= 512) {
+ event_data += message_data_len;
+ // 4 bytes for the tag, and max_payload_buf should be truncated.
+ ASSERT_LE(4 + 512, event_data - original); // worst expectations
+ ASSERT_GT(4 + data_len, event_data - original); // must be truncated
+ }
+ *found = true;
+ };
+
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+}
+#endif
+
+// Make multiple tests and re-tests orthogonal to prevent falsing.
+#ifdef TEST_LOGGER
+#define UNIQUE_TAG(X) \
+ (0x12340000 + (((X) + sizeof(int) + sizeof(void*)) << 8) + TEST_LOGGER)
+#else
+#define UNIQUE_TAG(X) \
+ (0x12340000 + (((X) + sizeof(int) + sizeof(void*)) << 8) + 0xBA)
+#endif
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+#ifdef __ANDROID__
+ android_errorWriteWithInfoLog_helper(UNIQUE_TAG(1), "test-subtag", -1, max_payload_buf, 200);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog,
+ android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
+#ifdef __ANDROID__
+ android_errorWriteWithInfoLog_helper(UNIQUE_TAG(2), "test-subtag", -1, max_payload_buf,
+ sizeof(max_payload_buf));
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+// TODO: Do we need to check that we didn't actually write anything if we return a failure here?
+TEST(liblog,
+ android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
+#ifdef __ANDROID__
+ int retval_android_errorWriteWithinInfoLog =
+ android_errorWriteWithInfoLog(UNIQUE_TAG(3), "test-subtag", -1, nullptr, 200);
+ ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog,
+ android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
+#ifdef __ANDROID__
+ android_errorWriteWithInfoLog_helper(
+ UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1, max_payload_buf, 200);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, __android_log_bswrite_and_print___max) {
+ bswrite_test(max_payload_buf);
+}
+
+TEST(liblog, __android_log_buf_write_and_print__max) {
+ buf_write_test(max_payload_buf);
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+#ifdef __ANDROID__
+ int kTag = UNIQUE_TAG(5);
+ const char* kSubTag = "test-subtag";
+
+ auto write_function = [&] {
+ int retval_android_errorWriteLog = android_errorWriteLog(kTag, kSubTag);
+ ASSERT_LT(0, retval_android_errorWriteLog);
+ };
+
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ char* event_data = log_msg.msg();
+
+ // Tag
+ auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
+ event_data += sizeof(android_event_header_t);
+ if (event_header->tag != kTag) {
+ return;
+ }
+
+ // List type
+ auto* event_list = reinterpret_cast<android_event_list_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_LIST, event_list->type);
+ ASSERT_EQ(3, event_list->element_count);
+ event_data += sizeof(android_event_list_t);
+
+ // Element #1: string type for subtag
+ auto* event_string_subtag = reinterpret_cast<android_event_string_t*>(event_data);
+ ASSERT_EQ(EVENT_TYPE_STRING, event_string_subtag->type);
+ int32_t subtag_len = strlen(kSubTag);
+ ASSERT_EQ(subtag_len, event_string_subtag->length);
+ if (memcmp(kSubTag, &event_string_subtag->data, subtag_len) == 0) {
+ *found = true;
+ }
+ };
+
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
+#ifdef __ANDROID__
+ EXPECT_LT(android_errorWriteLog(UNIQUE_TAG(6), nullptr), 0);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+// Do not retest logger list handling
+#ifdef __ANDROID__
+static int is_real_element(int type) {
+ return ((type == EVENT_TYPE_INT) || (type == EVENT_TYPE_LONG) ||
+ (type == EVENT_TYPE_STRING) || (type == EVENT_TYPE_FLOAT));
+}
+
+static int android_log_buffer_to_string(const char* msg, size_t len,
+ char* strOut, size_t strOutLen) {
+ android_log_context context = create_android_log_parser(msg, len);
+ android_log_list_element elem;
+ bool overflow = false;
+ /* Reserve 1 byte for null terminator. */
+ size_t origStrOutLen = strOutLen--;
+
+ if (!context) {
+ return -EBADF;
+ }
+
+ memset(&elem, 0, sizeof(elem));
+
+ size_t outCount;
+
+ do {
+ elem = android_log_read_next(context);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_LIST:
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = '[';
+ strOutLen--;
+ }
+ break;
+
+ case EVENT_TYPE_LIST_STOP:
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = ']';
+ strOutLen--;
+ }
+ break;
+
+ case EVENT_TYPE_INT:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1, "%" PRId32, elem.data.int32);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ case EVENT_TYPE_LONG:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1, "%" PRId64, elem.data.int64);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ case EVENT_TYPE_FLOAT:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1, "%f", elem.data.float32);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ default:
+ elem.complete = true;
+ break;
+
+ case EVENT_TYPE_UNKNOWN:
+#if 0 // Ideal purity in the test, we want to complain about UNKNOWN showing up
+ if (elem.complete) {
+ break;
+ }
+#endif
+ elem.data.string = const_cast<char*>("<unknown>");
+ elem.len = strlen(elem.data.string);
+ FALLTHROUGH_INTENDED;
+ case EVENT_TYPE_STRING:
+ if (elem.len <= strOutLen) {
+ memcpy(strOut, elem.data.string, elem.len);
+ strOut += elem.len;
+ strOutLen -= elem.len;
+ } else if (strOutLen > 0) {
+ /* copy what we can */
+ memcpy(strOut, elem.data.string, strOutLen);
+ strOut += strOutLen;
+ strOutLen = 0;
+ overflow = true;
+ }
+ break;
+ }
+
+ if (elem.complete) {
+ break;
+ }
+ /* Determine whether to put a comma or not. */
+ if (!overflow &&
+ (is_real_element(elem.type) || (elem.type == EVENT_TYPE_LIST_STOP))) {
+ android_log_list_element next = android_log_peek_next(context);
+ if (!next.complete &&
+ (is_real_element(next.type) || (next.type == EVENT_TYPE_LIST))) {
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = ',';
+ strOutLen--;
+ }
+ }
+ }
+ } while ((elem.type != EVENT_TYPE_UNKNOWN) && !overflow && !elem.complete);
+
+ android_log_destroy(&context);
+
+ if (overflow) {
+ if (strOutLen < origStrOutLen) {
+ /* leave an indicator */
+ *(strOut - 1) = '!';
+ } else {
+ /* nothing was written at all */
+ *strOut++ = '!';
+ }
+ }
+ *strOut++ = '\0';
+
+ if ((elem.type == EVENT_TYPE_UNKNOWN) && !elem.complete) {
+ fprintf(stderr, "Binary log entry conversion failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif // __ANDROID__
+
+#ifdef __ANDROID__
+static const char* event_test_int32(uint32_t tag, size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t);
+
+ return "1076895760";
+}
+
+static const char* event_test_int64(uint32_t tag, size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "-9191740941672636400";
+}
+
+static const char* event_test_list_int64(uint32_t tag, size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "[-9191740941672636400]";
+}
+
+static const char* event_test_simple_automagic_list(uint32_t tag,
+ size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ // The convenience API where we allow a simple list to be
+ // created without explicit begin or end calls.
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint8_t) +
+ sizeof(uint64_t);
+
+ return "[1076895760,-9191740941672636400]";
+}
+
+static const char* event_test_list_empty(uint32_t tag, size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t);
+
+ return "[]";
+}
+
+static const char* event_test_complex_nested_list(uint32_t tag,
+ size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+
+ EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x01020304));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x0102030405060708));
+ EXPECT_LE(0, android_log_write_string8(ctx, "Hello World"));
+ EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_end(ctx)); // ]
+ EXPECT_LE(0, android_log_write_float32(ctx, 1.0102030405060708));
+ EXPECT_LE(0, android_log_write_list_end(ctx)); // ]
+
+ //
+ // This one checks for the automagic list creation because a list
+ // begin and end was missing for it! This is actually an <oops> corner
+ // case, and not the behavior we morally support. The automagic API is to
+ // allow for a simple case of a series of objects in a single list. e.g.
+ // int32,int32,int32,string -> [int32,int32,int32,string]
+ //
+ EXPECT_LE(0, android_log_write_string8(ctx, "dlroW olleH"));
+
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint64_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") -
+ 1 + sizeof(uint8_t) + sizeof(uint8_t) +
+ 4 * (sizeof(uint8_t) + sizeof(uint32_t)) + sizeof(uint8_t) +
+ sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof("dlroW olleH") - 1;
+
+ return "[[16909060,72623859790382856,Hello World,[1,2,3,4],1.010203],dlroW "
+ "olleH]";
+}
+
+static const char* event_test_7_level_prefix(uint32_t tag,
+ size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 5));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 6));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 7));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + 7 * (sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t));
+
+ return "[[[[[[[1],2],3],4],5],6],7]";
+}
+
+static const char* event_test_7_level_suffix(uint32_t tag,
+ size_t& expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 5));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 6));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + 6 * (sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t));
+
+ return "[1,[2,[3,[4,[5,[6]]]]]]";
+}
+
+static const char* event_test_android_log_error_write(uint32_t tag,
+ size_t& expected_len) {
+ EXPECT_LE(
+ 0, __android_log_error_write(tag, "Hello World", 42, "dlroW olleH", 11));
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") -
+ 1 + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint8_t) +
+ sizeof(uint32_t) + sizeof("dlroW olleH") - 1;
+
+ return "[Hello World,42,dlroW olleH]";
+}
+
+static const char* event_test_android_log_error_write_null(uint32_t tag,
+ size_t& expected_len) {
+ EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, NULL, 0));
+
+ expected_len = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") -
+ 1 + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint8_t) +
+ sizeof(uint32_t) + sizeof("") - 1;
+
+ return "[Hello World,42,]";
+}
+
+// make sure all user buffers are flushed
+static void print_barrier() {
+ std::cout.flush();
+ fflush(stdout);
+ std::cerr.flush();
+ fflush(stderr); // everything else is paranoia ...
+}
+
+static void create_android_logger(const char* (*fn)(uint32_t tag,
+ size_t& expected_len)) {
+ size_t expected_len;
+ const char* expected_string;
+ auto write_function = [&] {
+ expected_string = (*fn)(1005, expected_len);
+ ASSERT_NE(nullptr, expected_string);
+ };
+
+ pid_t pid = getpid();
+ auto check_function = [&](log_msg log_msg, bool* found) {
+ if (static_cast<size_t>(log_msg.entry.len) != expected_len) {
+ return;
+ }
+
+ char* eventData = log_msg.msg();
+
+ AndroidLogFormat* logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ int processBinaryLogBuffer =
+ android_log_processBinaryLogBuffer(&log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ(0, processBinaryLogBuffer);
+ if (processBinaryLogBuffer == 0) {
+ int line_overhead = 20;
+ if (pid > 99999) ++line_overhead;
+ if (pid > 999999) ++line_overhead;
+ print_barrier();
+ int printLogLine =
+ android_log_printLogLine(logformat, fileno(stderr), &entry);
+ print_barrier();
+ EXPECT_EQ(line_overhead + (int)strlen(expected_string), printLogLine);
+ }
+ android_log_format_free(logformat);
+
+ // test buffer reading API
+ int buffer_to_string = -1;
+ if (eventData) {
+ auto* event_header = reinterpret_cast<android_event_header_t*>(eventData);
+ eventData += sizeof(android_event_header_t);
+ snprintf(msgBuf, sizeof(msgBuf), "I/[%" PRId32 "]", event_header->tag);
+ print_barrier();
+ fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
+ memset(msgBuf, 0, sizeof(msgBuf));
+ buffer_to_string =
+ android_log_buffer_to_string(eventData, log_msg.entry.len, msgBuf, sizeof(msgBuf));
+ fprintf(stderr, "%s\n", msgBuf);
+ print_barrier();
+ }
+ EXPECT_EQ(0, buffer_to_string);
+ EXPECT_STREQ(expected_string, msgBuf);
+ *found = true;
+ };
+
+ RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+}
+#endif
+
+TEST(liblog, create_android_logger_int32) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_int32);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_int64) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_int64);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_list_int64) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_list_int64);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_simple_automagic_list) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_simple_automagic_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_list_empty) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_list_empty);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_complex_nested_list) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_complex_nested_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_7_level_prefix) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_7_level_prefix);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_7_level_suffix) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_7_level_suffix);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_android_log_error_write) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_android_log_error_write);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_android_log_error_write_null) {
+#ifdef __ANDROID__
+ create_android_logger(event_test_android_log_error_write_null);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, create_android_logger_overflow) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
+ if (ctx) {
+ for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ }
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ /* One more for good measure, must be permanently unhappy */
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+ }
+
+ ASSERT_TRUE(NULL != (ctx = create_android_logger(1005)));
+ for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, i));
+ }
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ /* One more for good measure, must be permanently unhappy */
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ ASSERT_TRUE(NULL == ctx);
+}
+
+#ifdef ENABLE_FLAKY_TESTS
+#ifdef __ANDROID__
+#ifndef NO_PSTORE
+static const char __pmsg_file[] =
+ "/data/william-shakespeare/MuchAdoAboutNothing.txt";
+#endif /* NO_PSTORE */
+#endif
+
+TEST(liblog, __android_log_pmsg_file_write) {
+#ifdef __ANDROID__
+#ifndef NO_PSTORE
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+ int return__android_log_pmsg_file_write = __android_log_pmsg_file_write(
+ LOG_ID_CRASH, ANDROID_LOG_VERBOSE, __pmsg_file, max_payload_buf,
+ sizeof(max_payload_buf));
+ EXPECT_LT(0, return__android_log_pmsg_file_write);
+ if (return__android_log_pmsg_file_write == -ENOMEM) {
+ fprintf(stderr,
+ "Kernel does not have space allocated to pmsg pstore driver "
+ "configured\n");
+ } else if (!return__android_log_pmsg_file_write) {
+ fprintf(stderr,
+ "Reboot, ensure file %s matches\n"
+ "with liblog.__android_log_msg_file_read test\n",
+ __pmsg_file);
+ }
+ bool pmsgActiveAfter__android_pmsg_file_write;
+ bool logdwActiveAfter__android_pmsg_file_write;
+ if (getuid() == AID_ROOT) {
+ pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+ logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_pmsg_file_write);
+ EXPECT_FALSE(logdwActiveAfter__android_pmsg_file_write);
+ }
+ EXPECT_LT(
+ 0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_pmsg_file_write", "main"));
+ if (getuid() == AID_ROOT) {
+ bool pmsgActiveAfter__android_log_buf_print = isPmsgActive();
+ bool logdwActiveAfter__android_log_buf_print = isLogdwActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_log_buf_print);
+ EXPECT_TRUE(logdwActiveAfter__android_log_buf_print);
+ }
+ EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+ __pmsg_file, max_payload_buf,
+ sizeof(max_payload_buf)));
+ if (getuid() == AID_ROOT) {
+ pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+ logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+ EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
+ EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
+ }
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+#ifndef NO_PSTORE
+static ssize_t __pmsg_fn(log_id_t logId, char prio, const char* filename,
+ const char* buf, size_t len, void* arg) {
+ EXPECT_TRUE(NULL == arg);
+ EXPECT_EQ(LOG_ID_CRASH, logId);
+ EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
+ EXPECT_FALSE(NULL == strstr(__pmsg_file, filename));
+ EXPECT_EQ(len, sizeof(max_payload_buf));
+ EXPECT_STREQ(max_payload_buf, buf);
+
+ ++signaled;
+ if ((len != sizeof(max_payload_buf)) || strcmp(max_payload_buf, buf)) {
+ fprintf(stderr, "comparison fails on content \"%s\"\n", buf);
+ }
+ return arg || (LOG_ID_CRASH != logId) || (ANDROID_LOG_VERBOSE != prio) ||
+ !strstr(__pmsg_file, filename) ||
+ (len != sizeof(max_payload_buf)) ||
+ !!strcmp(max_payload_buf, buf)
+ ? -ENOEXEC
+ : 1;
+}
+#endif /* NO_PSTORE */
+#endif
+
+TEST(liblog, __android_log_pmsg_file_read) {
+#ifdef __ANDROID__
+#ifndef NO_PSTORE
+ signaled = 0;
+
+ __android_log_close();
+ if (getuid() == AID_ROOT) {
+ tested__android_log_close = true;
+ bool pmsgActiveAfter__android_log_close = isPmsgActive();
+ bool logdwActiveAfter__android_log_close = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+ EXPECT_FALSE(logdwActiveAfter__android_log_close);
+ } else if (!tested__android_log_close) {
+ fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+ }
+
+ ssize_t ret = __android_log_pmsg_file_read(LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+ __pmsg_file, __pmsg_fn, NULL);
+
+ if (getuid() == AID_ROOT) {
+ bool pmsgActiveAfter__android_log_pmsg_file_read = isPmsgActive();
+ bool logdwActiveAfter__android_log_pmsg_file_read = isLogdwActive();
+ EXPECT_FALSE(pmsgActiveAfter__android_log_pmsg_file_read);
+ EXPECT_FALSE(logdwActiveAfter__android_log_pmsg_file_read);
+ }
+
+ if (ret == -ENOENT) {
+ fprintf(stderr,
+ "No pre-boot results of liblog.__android_log_mesg_file_write to "
+ "compare with,\n"
+ "false positive test result.\n");
+ return;
+ }
+
+ EXPECT_LT(0, ret);
+ EXPECT_EQ(1U, signaled);
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // ENABLE_FLAKY_TESTS
+
+TEST(liblog, android_lookupEventTagNum) {
+#ifdef __ANDROID__
+ EventTagMap* map = android_openEventTagMap(NULL);
+ EXPECT_TRUE(NULL != map);
+ std::string Name = android::base::StringPrintf("a%d", getpid());
+ int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)",
+ ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
+ EXPECT_NE(-1, tag);
+ EXPECT_NE(0, tag);
+ EXPECT_GT(UINT32_MAX, (unsigned)tag);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
new file mode 100644
index 000000000..9fb5a2c75
--- /dev/null
+++ b/liblog/tests/log_id_test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_id.h>
+
+// We do not want to include <android/log.h> to acquire ANDROID_LOG_INFO for
+// include file API purity. We do however want to allow the _option_ that
+// log/log_id.h could include this file, or related content, in the future.
+#ifndef __android_LogPriority_defined
+#define ANDROID_LOG_INFO 4
+#endif
+
+TEST(liblog, log_id) {
+ int count = 0;
+
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ log_id_t id = static_cast<log_id_t>(i);
+ const char* name = android_log_id_to_name(id);
+ if (id != android_name_to_log_id(name)) {
+ continue;
+ }
+ ++count;
+ fprintf(stderr, "log buffer %s\r", name);
+ }
+ ASSERT_EQ(LOG_ID_MAX, count);
+}
+
+TEST(liblog, __android_log_buf_print) {
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print", "radio"));
+ usleep(1000);
+ EXPECT_LT(0,
+ __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print", "system"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_print", "main"));
+ usleep(1000);
+}
+
+TEST(liblog, __android_log_buf_write) {
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write", "radio"));
+ usleep(1000);
+ EXPECT_LT(0,
+ __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write", "system"));
+ usleep(1000);
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ "TEST__android_log_buf_write", "main"));
+ usleep(1000);
+}
+
+static void* ConcurrentPrintFn(void* arg) {
+ int ret = __android_log_buf_print(
+ LOG_ID_MAIN, ANDROID_LOG_INFO, "TEST__android_log_print",
+ "Concurrent %" PRIuPTR, reinterpret_cast<uintptr_t>(arg));
+ return reinterpret_cast<void*>(ret);
+}
+
+#define NUM_CONCURRENT 64
+#define _concurrent_name(a, n) a##__concurrent##n
+#define concurrent_name(a, n) _concurrent_name(a, n)
+
+TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
+ pthread_t t[NUM_CONCURRENT];
+ int i;
+ for (i = 0; i < NUM_CONCURRENT; i++) {
+ ASSERT_EQ(0, pthread_create(&t[i], NULL, ConcurrentPrintFn,
+ reinterpret_cast<void*>(i)));
+ }
+ int ret = 1;
+ for (i = 0; i < NUM_CONCURRENT; i++) {
+ void* result;
+ ASSERT_EQ(0, pthread_join(t[i], &result));
+ int this_result = reinterpret_cast<uintptr_t>(result);
+ if ((0 < ret) && (ret != this_result)) {
+ ret = this_result;
+ }
+ }
+ ASSERT_LT(0, ret);
+}
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
new file mode 100644
index 000000000..fa1255e0c
--- /dev/null
+++ b/liblog/tests/log_radio_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_radio.h>
+
+TEST(liblog, RLOG) {
+ static const char content[] = "log_radio.h";
+ static const char content_false[] = "log_radio.h false";
+
+// ratelimit content to 10/s to keep away from spam filters
+// do not send identical content together to keep away from spam filters
+
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGV"
+ RLOGV(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGD"
+ RLOGD(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGI"
+ RLOGI(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGW"
+ RLOGW(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGE"
+ RLOGE(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGV"
+ RLOGV_IF(true, content);
+ usleep(100000);
+ RLOGV_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGD"
+ RLOGD_IF(true, content);
+ usleep(100000);
+ RLOGD_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGI"
+ RLOGI_IF(true, content);
+ usleep(100000);
+ RLOGI_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGW"
+ RLOGW_IF(true, content);
+ usleep(100000);
+ RLOGW_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__RLOGE"
+ RLOGE_IF(true, content);
+ usleep(100000);
+ RLOGE_IF(false, content_false);
+
+#ifdef __ANDROID__
+ // give time for content to long-path through logger
+ sleep(1);
+
+ std::string buf = android::base::StringPrintf(
+ "logcat -b radio --pid=%u -d -s"
+ " TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
+ (unsigned)getpid());
+ FILE* fp = popen(buf.c_str(), "re");
+ int count = 0;
+ int count_false = 0;
+ if (fp) {
+ if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
+ pclose(fp);
+ for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos;
+ ++pos) {
+ ++count;
+ }
+ for (size_t pos = 0;
+ (pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
+ ++count_false;
+ }
+ }
+ EXPECT_EQ(0, count_false);
+#if LOG_NDEBUG
+ ASSERT_EQ(8, count);
+#else
+ ASSERT_EQ(10, count);
+#endif
+
+#else
+ GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
+#endif
+}
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
new file mode 100644
index 000000000..1be99aa80
--- /dev/null
+++ b/liblog/tests/log_read_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <android/log.h> // minimal logging API
+#include <gtest/gtest.h>
+#include <log/log_properties.h>
+// Test the APIs in this standalone include file
+#include <log/log_read.h>
+// Do not use anything in log/log_time.h despite side effects of the above.
+#include <private/android_logger.h>
+
+TEST(liblog, android_logger_get_) {
+#ifdef __ANDROID__
+ // This test assumes the log buffers are filled with noise from
+ // normal operations. It will fail if done immediately after a
+ // logcat -c.
+ struct logger_list* logger_list =
+ android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0);
+
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ log_id_t id = static_cast<log_id_t>(i);
+ const char* name = android_log_id_to_name(id);
+ if (id != android_name_to_log_id(name)) {
+ continue;
+ }
+ fprintf(stderr, "log buffer %s\r", name);
+ struct logger* logger;
+ EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
+ EXPECT_EQ(id, android_logger_get_id(logger));
+ ssize_t get_log_size = android_logger_get_log_size(logger);
+ /* security buffer is allowed to be denied */
+ if (strcmp("security", name)) {
+ EXPECT_LT(0, get_log_size);
+ // crash buffer is allowed to be empty, that is actually healthy!
+ // kernel buffer is allowed to be empty on "user" builds
+ // stats buffer is allowed to be empty TEMPORARILY.
+ // TODO: remove stats buffer from here once we start to use it in
+ // framework (b/68266385).
+ EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
+ !!((strcmp("crash", name) != 0) &&
+ ((strcmp("kernel", name) != 0) ||
+ __android_logger_property_get_bool(
+ "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE)) &&
+ (strcmp("stats", name) != 0)),
+ android_logger_get_log_readable_size(logger));
+ } else {
+ EXPECT_NE(0, get_log_size);
+ if (get_log_size < 0) {
+ EXPECT_GT(0, android_logger_get_log_readable_size(logger));
+ } else {
+ EXPECT_LE(0, android_logger_get_log_readable_size(logger));
+ }
+ }
+ EXPECT_LT(0, android_logger_get_log_version(logger));
+ }
+
+ android_logger_list_close(logger_list);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
new file mode 100644
index 000000000..13f026d34
--- /dev/null
+++ b/liblog/tests/log_system_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_system.h>
+
+TEST(liblog, SLOG) {
+ static const char content[] = "log_system.h";
+ static const char content_false[] = "log_system.h false";
+
+// ratelimit content to 10/s to keep away from spam filters
+// do not send identical content together to keep away from spam filters
+
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGV"
+ SLOGV(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGD"
+ SLOGD(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGI"
+ SLOGI(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGW"
+ SLOGW(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGE"
+ SLOGE(content);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGV"
+ SLOGV_IF(true, content);
+ usleep(100000);
+ SLOGV_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGD"
+ SLOGD_IF(true, content);
+ usleep(100000);
+ SLOGD_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGI"
+ SLOGI_IF(true, content);
+ usleep(100000);
+ SLOGI_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGW"
+ SLOGW_IF(true, content);
+ usleep(100000);
+ SLOGW_IF(false, content_false);
+ usleep(100000);
+#undef LOG_TAG
+#define LOG_TAG "TEST__SLOGE"
+ SLOGE_IF(true, content);
+ usleep(100000);
+ SLOGE_IF(false, content_false);
+
+#ifdef __ANDROID__
+ // give time for content to long-path through logger
+ sleep(1);
+
+ std::string buf = android::base::StringPrintf(
+ "logcat -b system --pid=%u -d -s"
+ " TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
+ (unsigned)getpid());
+ FILE* fp = popen(buf.c_str(), "re");
+ int count = 0;
+ int count_false = 0;
+ if (fp) {
+ if (!android::base::ReadFdToString(fileno(fp), &buf)) buf = "";
+ pclose(fp);
+ for (size_t pos = 0; (pos = buf.find(content, pos)) != std::string::npos;
+ ++pos) {
+ ++count;
+ }
+ for (size_t pos = 0;
+ (pos = buf.find(content_false, pos)) != std::string::npos; ++pos) {
+ ++count_false;
+ }
+ }
+ EXPECT_EQ(0, count_false);
+#if LOG_NDEBUG
+ ASSERT_EQ(8, count);
+#else
+ ASSERT_EQ(10, count);
+#endif
+
+#else
+ GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
+#endif
+}
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
new file mode 100644
index 000000000..47fe594ab
--- /dev/null
+++ b/liblog/tests/log_time_test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <time.h>
+
+#include <gtest/gtest.h>
+// Test the APIs in this standalone include file
+#include <log/log_time.h>
+
+TEST(liblog, log_time) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ log_time tl(ts);
+
+ EXPECT_EQ(tl, ts);
+ EXPECT_GE(tl, ts);
+ EXPECT_LE(tl, ts);
+}
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
new file mode 100644
index 000000000..e06964f72
--- /dev/null
+++ b/liblog/tests/log_wrap_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h> // minimal logging API
+#include <gtest/gtest.h>
+#include <log/log_properties.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+
+#ifdef __ANDROID__
+static void read_with_wrap() {
+ // Read the last line in the log to get a starting timestamp. We're assuming
+ // the log is not empty.
+ const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ struct logger_list* logger_list =
+ android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
+
+ ASSERT_NE(logger_list, nullptr);
+
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ android_logger_list_close(logger_list);
+ ASSERT_GT(ret, 0);
+
+ log_time start(log_msg.entry.sec, log_msg.entry.nsec);
+ ASSERT_NE(start, log_time());
+
+ logger_list =
+ android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0);
+ ASSERT_NE(logger_list, nullptr);
+
+ struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN);
+ EXPECT_NE(logger, nullptr);
+ if (logger) {
+ android_logger_list_read(logger_list, &log_msg);
+ }
+
+ android_logger_list_close(logger_list);
+}
+#endif
+
+// b/64143705 confirm fixed
+TEST(liblog, wrap_mode_blocks) {
+#ifdef __ANDROID__
+ // The read call is expected to take up to 2 hours in the happy case. There was a previous bug
+ // where it would take only 30 seconds due to an alarm() in logd_reader.cpp. That alarm has been
+ // removed, so we check here that the read call blocks for a reasonable amount of time (5s).
+
+ struct sigaction ignore = {.sa_handler = [](int) { _exit(0); }};
+ struct sigaction old_sigaction;
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ alarm(5);
+
+ android::base::Timer timer;
+ read_with_wrap();
+
+ FAIL() << "read_with_wrap() should not return before the alarm is triggered.";
+
+ alarm(0);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 000000000..b8e472650
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket. This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ auto temp_dir = TemporaryDir();
+ auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+ unique_fd server_socket;
+
+ auto open_server_socket = [&] {
+ server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+ ASSERT_TRUE(server_socket.ok());
+
+ sockaddr_un server_sockaddr = {};
+ server_sockaddr.sun_family = AF_UNIX;
+ strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+ sizeof(server_sockaddr))));
+ };
+
+ // Open the server socket.
+ open_server_socket();
+
+ // Open the client socket.
+ auto client_socket =
+ unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+ ASSERT_TRUE(client_socket.ok());
+ sockaddr_un client_sockaddr = {};
+ client_sockaddr.sun_family = AF_UNIX;
+ strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))));
+
+ // Ensure that communication works.
+ constexpr static char kSmoke[] = "smoke test";
+ ssize_t smoke_len = sizeof(kSmoke);
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ char read_buf[512];
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+
+ // Close the server socket.
+ server_socket.reset();
+ ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+ // Ensure that write() from the client returns an error since the server is closed.
+ ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+ // Open the server socket again.
+ open_server_socket();
+
+ // Reconnect the same client socket.
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))))
+ << strerror(errno);
+
+ // Ensure that communication works.
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+} \ No newline at end of file
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
new file mode 100644
index 000000000..72e53f9bd
--- /dev/null
+++ b/liblog/tests/logprint_test.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/logprint.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/log_read.h>
+
+size_t convertPrintable(char* p, const char* message, size_t messageLen);
+
+TEST(liblog, convertPrintable_ascii) {
+ auto input = "easy string, output same";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_escapes) {
+ // Note that \t is not escaped.
+ auto input = "escape\a\b\t\v\f\r\\";
+ auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_validutf8) {
+ auto input = u8"¢ह€𐍈";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_invalidutf8) {
+ auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
+ auto expected_output =
+ "\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_mixed) {
+ auto input =
+ u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
+ auto expected_output =
+ u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
+ u8"\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, log_print_different_header_size) {
+ constexpr int32_t kPid = 123;
+ constexpr uint32_t kTid = 456;
+ constexpr uint32_t kSec = 1000;
+ constexpr uint32_t kNsec = 999;
+ constexpr uint32_t kLid = LOG_ID_MAIN;
+ constexpr uint32_t kUid = 987;
+ constexpr char kPriority = ANDROID_LOG_ERROR;
+
+ auto create_buf = [](char* buf, size_t len, uint16_t hdr_size) {
+ memset(buf, 0, len);
+ logger_entry* header = reinterpret_cast<logger_entry*>(buf);
+ header->hdr_size = hdr_size;
+ header->pid = kPid;
+ header->tid = kTid;
+ header->sec = kSec;
+ header->nsec = kNsec;
+ header->lid = kLid;
+ header->uid = kUid;
+ char* message = buf + header->hdr_size;
+ uint16_t message_len = 0;
+ message[message_len++] = kPriority;
+ message[message_len++] = 'T';
+ message[message_len++] = 'a';
+ message[message_len++] = 'g';
+ message[message_len++] = '\0';
+ message[message_len++] = 'm';
+ message[message_len++] = 's';
+ message[message_len++] = 'g';
+ message[message_len++] = '!';
+ message[message_len++] = '\0';
+ header->len = message_len;
+ };
+
+ auto check_entry = [&](const AndroidLogEntry& entry) {
+ EXPECT_EQ(kSec, static_cast<uint32_t>(entry.tv_sec));
+ EXPECT_EQ(kNsec, static_cast<uint32_t>(entry.tv_nsec));
+ EXPECT_EQ(kPriority, entry.priority);
+ EXPECT_EQ(kUid, static_cast<uint32_t>(entry.uid));
+ EXPECT_EQ(kPid, entry.pid);
+ EXPECT_EQ(kTid, static_cast<uint32_t>(entry.tid));
+ EXPECT_STREQ("Tag", entry.tag);
+ EXPECT_EQ(4U, entry.tagLen); // Apparently taglen includes the nullptr?
+ EXPECT_EQ(4U, entry.messageLen);
+ EXPECT_STREQ("msg!", entry.message);
+ };
+ alignas(logger_entry) char buf[LOGGER_ENTRY_MAX_LEN];
+ create_buf(buf, sizeof(buf), sizeof(logger_entry));
+
+ AndroidLogEntry entry_normal_size;
+ ASSERT_EQ(0,
+ android_log_processLogBuffer(reinterpret_cast<logger_entry*>(buf), &entry_normal_size));
+ check_entry(entry_normal_size);
+
+ create_buf(buf, sizeof(buf), sizeof(logger_entry) + 3);
+ AndroidLogEntry entry_odd_size;
+ ASSERT_EQ(0, android_log_processLogBuffer(reinterpret_cast<logger_entry*>(buf), &entry_odd_size));
+ check_entry(entry_odd_size);
+} \ No newline at end of file
diff --git a/liblog/uio.h b/liblog/uio.h
new file mode 100644
index 000000000..c85893cf4
--- /dev/null
+++ b/liblog/uio.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#if defined(_WIN32)
+#include <stddef.h>
+struct iovec {
+ void* iov_base;
+ size_t iov_len;
+};
+#else
+#include <sys/uio.h>
+#endif
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index ba11dc920..78da46cf1 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libmodprobe",
cflags: [
diff --git a/libmodprobe/OWNERS b/libmodprobe/OWNERS
index a6796cbba..4b770b1c3 100644
--- a/libmodprobe/OWNERS
+++ b/libmodprobe/OWNERS
@@ -1,2 +1,2 @@
-dvander@google.com
-willmcvicker@google.com
+tomcherry@google.com
+smuckle@google.com
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index c934860ec..4806b08b4 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -24,8 +24,7 @@
class Modprobe {
public:
- Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
- bool use_blocklist = true);
+ Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load");
bool LoadListedModules(bool strict = true);
bool LoadWithAliases(const std::string& module_name, bool strict,
@@ -37,6 +36,8 @@ class Modprobe {
std::vector<std::string>* post_dependencies);
void ResetModuleCount() { module_count_ = 0; }
int GetModuleCount() { return module_count_; }
+ void EnableBlocklist(bool enable);
+ void EnableVerbose(bool enable);
private:
std::string MakeCanonical(const std::string& module_path);
@@ -48,7 +49,6 @@ class Modprobe {
void AddOption(const std::string& module_name, const std::string& option_name,
const std::string& value);
std::string GetKernelCmdline();
- bool IsBlocklisted(const std::string& module_name);
bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
bool ParseAliasCallback(const std::vector<std::string>& args);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1a9d3642c..5a6ae8be1 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -66,7 +66,6 @@ bool Modprobe::ParseDepCallback(const std::string& base_path,
deps.emplace_back(prefix + args[0].substr(0, pos));
} else {
LOG(ERROR) << "dependency lines must start with name followed by ':'";
- return false;
}
// Remaining items are dependencies of our module
@@ -313,9 +312,7 @@ void Modprobe::ParseKernelCmdlineOptions(void) {
}
}
-Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file,
- bool use_blocklist)
- : blocklist_enabled(use_blocklist) {
+Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file) {
using namespace std::placeholders;
for (const auto& base_path : base_paths) {
@@ -339,6 +336,19 @@ Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string
}
ParseKernelCmdlineOptions();
+ android::base::SetMinimumLogSeverity(android::base::INFO);
+}
+
+void Modprobe::EnableBlocklist(bool enable) {
+ blocklist_enabled = enable;
+}
+
+void Modprobe::EnableVerbose(bool enable) {
+ if (enable) {
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ } else {
+ android::base::SetMinimumLogSeverity(android::base::INFO);
+ }
}
std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
@@ -425,23 +435,10 @@ bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,
return true;
}
-bool Modprobe::IsBlocklisted(const std::string& module_name) {
- if (!blocklist_enabled) return false;
-
- auto canonical_name = MakeCanonical(module_name);
- auto dependencies = GetDependencies(canonical_name);
- for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
- if (module_blocklist_.count(MakeCanonical(*dep))) return true;
- }
-
- return module_blocklist_.count(canonical_name) > 0;
-}
-
bool Modprobe::LoadListedModules(bool strict) {
auto ret = true;
for (const auto& module : module_load_) {
if (!LoadWithAliases(module, true)) {
- if (IsBlocklisted(module)) continue;
ret = false;
if (strict) break;
}
@@ -451,10 +448,16 @@ bool Modprobe::LoadListedModules(bool strict) {
bool Modprobe::Remove(const std::string& module_name) {
auto dependencies = GetDependencies(MakeCanonical(module_name));
- for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {
+ if (dependencies.empty()) {
+ LOG(ERROR) << "Empty dependencies for module " << module_name;
+ return false;
+ }
+ if (!Rmmod(dependencies[0])) {
+ return false;
+ }
+ for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) {
Rmmod(*dep);
}
- Rmmod(module_name);
return true;
}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index fb1f5e73e..84f71506a 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -35,7 +35,7 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
- PLOG(ERROR) << "Could not open module '" << path_name << "'";
+ LOG(ERROR) << "Could not open module '" << path_name << "'";
return false;
}
@@ -49,7 +49,7 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter
options = options + " " + parameters;
}
- LOG(INFO) << "Loading module " << path_name << " with args '" << options << "'";
+ LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
if (ret != 0) {
if (errno == EEXIST) {
@@ -57,7 +57,7 @@ bool Modprobe::Insmod(const std::string& path_name, const std::string& parameter
module_loaded_.emplace(canonical_name);
return true;
}
- PLOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
+ LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
return false;
}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index f960b6139..5919c49be 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -78,18 +78,6 @@ TEST(libmodprobe, Test) {
"/test13.ko",
};
- std::vector<std::string> expected_modules_blocklist_enabled = {
- "/test1.ko option1=50 option2=60",
- "/test6.ko",
- "/test2.ko",
- "/test5.ko option1=",
- "/test8.ko",
- "/test7.ko param1=4",
- "/test12.ko",
- "/test11.ko",
- "/test13.ko",
- };
-
const std::string modules_dep =
"test1.ko:\n"
"test2.ko:\n"
@@ -158,7 +146,7 @@ TEST(libmodprobe, Test) {
*i = dir.path + *i;
}
- Modprobe m({dir.path}, "modules.load", false);
+ Modprobe m({dir.path});
EXPECT_TRUE(m.LoadListedModules());
GTEST_LOG_(INFO) << "Expected modules loaded (in order):";
@@ -188,33 +176,6 @@ TEST(libmodprobe, Test) {
EXPECT_TRUE(modules_loaded == expected_after_remove);
- m = Modprobe({dir.path});
+ m.EnableBlocklist(true);
EXPECT_FALSE(m.LoadWithAliases("test4", true));
- while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
- EXPECT_TRUE(m.LoadListedModules());
-
- GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
- for (auto i = expected_modules_blocklist_enabled.begin();
- i != expected_modules_blocklist_enabled.end(); ++i) {
- *i = dir.path + *i;
- GTEST_LOG_(INFO) << "\"" << *i << "\"";
- }
- GTEST_LOG_(INFO) << "Actual modules loaded with blocklist enabled (in order):";
- for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
- GTEST_LOG_(INFO) << "\"" << *i << "\"";
- }
- EXPECT_TRUE(modules_loaded == expected_modules_blocklist_enabled);
-}
-
-TEST(libmodprobe, ModuleDepLineWithoutColonIsSkipped) {
- TemporaryDir dir;
- auto dir_path = std::string(dir.path);
- ASSERT_TRUE(android::base::WriteStringToFile(
- "no_colon.ko no_colon.ko\n", dir_path + "/modules.dep", 0600, getuid(), getgid()));
-
- kernel_cmdline = "";
- test_modules = {dir_path + "/no_colon.ko"};
-
- Modprobe m({dir.path});
- EXPECT_FALSE(m.LoadWithAliases("no_colon", true));
}
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 2864ad050..65371fa63 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,20 +1,3 @@
-package {
- default_applicable_licenses: ["system_core_libnetutils_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_core_libnetutils_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_library_shared {
name: "libnetutils",
vendor_available: true,
@@ -38,11 +21,6 @@ cc_library_shared {
cflags: ["-Werror"],
export_include_dirs: ["include"],
- // TODO: remove connectivity module dependency, or have this lib build against the ndk
- apex_available: [
- "//apex_available:platform",
- "com.android.tethering",
- ],
}
cc_library_static {
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 81885d75c..b56dcdb95 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,11 +1,5 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libpackagelistparser",
- ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
srcs: ["packagelistparser.cpp"],
shared_libs: ["liblog"],
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c68552d11..bda11e91d 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,13 +1,6 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libprocessgroup_headers",
vendor_available: true,
- product_available: true,
- ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
@@ -37,11 +30,8 @@ cc_library {
name: "libprocessgroup",
host_supported: true,
native_bridge_supported: true,
- ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
vendor_available: true,
- product_available: true,
vndk: {
enabled: true,
support_system_process: true,
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index 8ebb8cc9c..27b9a03a0 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,2 +1,3 @@
ccross@google.com
surenb@google.com
+tomcherry@google.com
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 5ca096787..2fc920b54 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -71,7 +71,7 @@ bool CgroupController::IsUsable() {
if (!HasValue()) return false;
if (state_ == UNKNOWN) {
- if (__builtin_available(android 30, *)) {
+ if (ACgroupController_getFlags != nullptr) {
uint32_t flags = ACgroupController_getFlags(controller_);
state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;
} else {
@@ -115,13 +115,7 @@ bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
return true;
}
- std::string cg_tag;
-
- if (version() == 2) {
- cg_tag = "0::";
- } else {
- cg_tag = StringPrintf(":%s:", name());
- }
+ std::string cg_tag = StringPrintf(":%s:", name());
size_t start_pos = content.find(cg_tag);
if (start_pos == std::string::npos) {
return false;
@@ -172,7 +166,7 @@ void CgroupMap::Print() const {
auto controller_count = ACgroupFile_getControllerCount();
for (uint32_t i = 0; i < controller_count; ++i) {
const ACgroupController* controller = ACgroupFile_getController(i);
- if (__builtin_available(android 30, *)) {
+ if (ACgroupController_getFlags != nullptr) {
LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
<< ACgroupController_getVersion(controller) << " path "
<< ACgroupController_getPath(controller) << " flags "
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
index 7522cfe39..0af75bb46 100644
--- a/libprocessgroup/cgrouprc/Android.bp
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libcgrouprc",
host_supported: true,
- ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
// Do not ever mark this as vendor_available; otherwise, vendor modules
// that links to the static library will behave unexpectedly. All on-device
@@ -28,9 +22,6 @@ cc_library {
// defined below. The static library is built for tests.
vendor_available: false,
native_bridge_supported: true,
- llndk: {
- symbol_file: "libcgrouprc.map.txt",
- },
srcs: [
"cgroup_controller.cpp",
"cgroup_file.cpp",
@@ -52,12 +43,21 @@ cc_library {
"libcgrouprc_format",
],
stubs: {
- symbol_file: "libcgrouprc.map.txt",
+ symbol_file: "libcgrouprc.llndk.txt",
versions: ["29"],
},
target: {
linux: {
- version_script: "libcgrouprc.map.txt",
+ version_script: "libcgrouprc.llndk.txt",
},
},
}
+
+llndk_library {
+ name: "libcgrouprc",
+ symbol_file: "libcgrouprc.llndk.txt",
+ native_bridge_supported: true,
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 100d60ea9..0ce51237a 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -28,6 +28,8 @@ __BEGIN_DECLS
struct ACgroupController;
typedef struct ACgroupController ACgroupController;
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
// ACgroupFile
/**
@@ -67,8 +69,8 @@ __attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const
* Flag bitmask used in ACgroupController_getFlags
*/
#define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1
-#define CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION 0x2
-#define CGROUPRC_CONTROLLER_FLAG_OPTIONAL 0x4
+
+#if __ANDROID_API__ >= __ANDROID_API_R__
/**
* Returns the flags bitmask of the given controller.
@@ -77,6 +79,8 @@ __attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const
__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getFlags(
const ACgroupController*) __INTRODUCED_IN(30);
+#endif
+
/**
* Returns the name of the given controller.
* If the given controller is null, return nullptr.
@@ -92,3 +96,5 @@ __attribute__((warn_unused_result)) const char* ACgroupController_getPath(const
__INTRODUCED_IN(29);
__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
index ce5c419a3..ce5c419a3 100644
--- a/libprocessgroup/cgrouprc/libcgrouprc.map.txt
+++ b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
index 059092419..559a869f8 100644
--- a/libprocessgroup/cgrouprc_format/Android.bp
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libcgrouprc_format",
host_supported: true,
- ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
native_bridge_supported: true,
srcs: [
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index fa2642d86..4aa439a6f 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -65,10 +65,6 @@ bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
void removeAllProcessGroups(void);
-// Provides the path for an attribute in a specific process group
-// Returns false in case of error, true in case of success
-bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
-
#endif // __ANDROID_VNDK__
__END_DECLS
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
index a18847ee7..945d90ce3 100644
--- a/libprocessgroup/include/processgroup/sched_policy.h
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -42,7 +42,7 @@ typedef enum {
SP_DEFAULT = -1,
SP_BACKGROUND = 0,
SP_FOREGROUND = 1,
- SP_SYSTEM = 2,
+ SP_SYSTEM = 2, // can't be used with set_sched_policy()
SP_AUDIO_APP = 3,
SP_AUDIO_SYS = 4,
SP_TOP_APP = 5,
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index c824376e5..d669ebe7f 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -131,20 +131,14 @@ static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
}
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
- int ret = 0;
- auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
- auto uid_path = ConvertUidToPath(cgroup, uid);
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
+ int ret;
- if (retries == 0) {
- retries = 1;
- }
+ auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
+ ret = rmdir(uid_pid_path.c_str());
- while (retries--) {
- ret = rmdir(uid_pid_path.c_str());
- if (!ret || errno != EBUSY) break;
- std::this_thread::sleep_for(5ms);
- }
+ auto uid_path = ConvertUidToPath(cgroup, uid);
+ rmdir(uid_path.c_str());
return ret;
}
@@ -182,7 +176,7 @@ void removeAllProcessGroups() {
std::vector<std::string> cgroups;
std::string path;
- if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
+ if (CgroupGetControllerPath("cpuacct", &path)) {
cgroups.push_back(path);
}
if (CgroupGetControllerPath("memory", &path)) {
@@ -218,53 +212,19 @@ void removeAllProcessGroups() {
}
}
-/**
- * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
- * the user root. Ownership for the newly created cgroup and all of its files must thus be
- * transferred for the user/group passed as uid/gid before system_server can properly access them.
- */
static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
- if (mkdir(path.c_str(), mode) == -1) {
- if (errno == EEXIST) {
- // Directory already exists and permissions have been set at the time it was created
- return true;
- }
+ if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
return false;
}
- auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
-
- if (dir == NULL) {
- PLOG(ERROR) << "opendir failed for " << path;
- goto err;
- }
-
- struct dirent* dir_entry;
- while ((dir_entry = readdir(dir.get()))) {
- if (!strcmp("..", dir_entry->d_name)) {
- continue;
- }
-
- std::string file_path = path + "/" + dir_entry->d_name;
-
- if (lchown(file_path.c_str(), uid, gid) < 0) {
- PLOG(ERROR) << "lchown failed for " << file_path;
- goto err;
- }
-
- if (fchmodat(AT_FDCWD, file_path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
- PLOG(ERROR) << "fchmodat failed for " << file_path;
- goto err;
- }
+ if (chown(path.c_str(), uid, gid) == -1) {
+ int saved_errno = errno;
+ rmdir(path.c_str());
+ errno = saved_errno;
+ return false;
}
return true;
-err:
- int saved_errno = errno;
- rmdir(path.c_str());
- errno = saved_errno;
-
- return false;
}
// Returns number of processes killed on success
@@ -342,9 +302,17 @@ static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid,
static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
int* max_processes) {
- std::string hierarchy_root_path;
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
- const char* cgroup = hierarchy_root_path.c_str();
+ std::string cpuacct_path;
+ std::string memory_path;
+
+ CgroupGetControllerPath("cpuacct", &cpuacct_path);
+ CgroupGetControllerPath("memory", &memory_path);
+ memory_path += "/apps";
+
+ const char* cgroup =
+ (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
+ ? cpuacct_path.c_str()
+ : memory_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
@@ -387,17 +355,7 @@ static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms";
}
-
- int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
-
- if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
- std::string memory_path;
- CgroupGetControllerPath("memory", &memory_path);
- memory_path += "/apps";
- if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
- }
-
- return err;
+ return RemoveProcessGroup(cgroup, uid, initialPid);
} else {
if (retries > 0) {
LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -416,30 +374,25 @@ int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_process
return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
}
-static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
- auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
-
- struct stat cgroup_stat;
- mode_t cgroup_mode = 0750;
- gid_t cgroup_uid = AID_SYSTEM;
- uid_t cgroup_gid = AID_SYSTEM;
-
- if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
- PLOG(ERROR) << "Failed to get stats for " << cgroup;
+int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
+ std::string cgroup;
+ if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
+ CgroupGetControllerPath("memory", &cgroup);
+ cgroup += "/apps";
} else {
- cgroup_mode = cgroup_stat.st_mode;
- cgroup_uid = cgroup_stat.st_uid;
- cgroup_gid = cgroup_stat.st_gid;
+ CgroupGetControllerPath("cpuacct", &cgroup);
}
- if (!MkdirAndChown(uid_path, cgroup_mode, cgroup_uid, cgroup_gid)) {
+ auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
+
+ if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
- if (!MkdirAndChown(uid_pid_path, cgroup_mode, cgroup_uid, cgroup_gid)) {
+ if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
return -errno;
}
@@ -455,27 +408,6 @@ static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgr
return ret;
}
-int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
- std::string cgroup;
-
- if (memControl && !UsePerAppMemcg()) {
- PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
- return -EINVAL;
- }
-
- if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
- CgroupGetControllerPath("memory", &cgroup);
- cgroup += "/apps";
- int ret = createProcessGroupInternal(uid, initialPid, cgroup);
- if (ret != 0) {
- return ret;
- }
- }
-
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
- return createProcessGroupInternal(uid, initialPid, cgroup);
-}
-
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
if (!isMemoryCgroupSupported()) {
PLOG(ERROR) << "Memcg is not mounted.";
@@ -506,7 +438,3 @@ bool setProcessGroupSoftLimit(uid_t, int pid, int64_t soft_limit_in_bytes) {
bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes);
}
-
-bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
- return CgroupGetAttributePathForTask(attr_name, tid, path);
-}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 885971a7a..ccc6f62f5 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -12,18 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
prebuilt_etc {
name: "cgroups.json",
src: "cgroups.json",
- required: [
- "cgroups_28.json",
- "cgroups_29.json",
- "cgroups_30.json",
- ],
}
prebuilt_etc {
@@ -34,49 +25,8 @@ prebuilt_etc {
}
prebuilt_etc {
- name: "cgroups_28.json",
- src: "cgroups_28.json",
- sub_dir: "task_profiles",
-}
-
-prebuilt_etc {
- name: "cgroups_29.json",
- src: "cgroups_29.json",
- sub_dir: "task_profiles",
-}
-
-prebuilt_etc {
- name: "cgroups_30.json",
- src: "cgroups_30.json",
- sub_dir: "task_profiles",
-}
-
-prebuilt_etc {
name: "task_profiles.json",
src: "task_profiles.json",
- required: [
- "task_profiles_28.json",
- "task_profiles_29.json",
- "task_profiles_30.json",
- ],
-}
-
-prebuilt_etc {
- name: "task_profiles_28.json",
- src: "task_profiles_28.json",
- sub_dir: "task_profiles",
-}
-
-prebuilt_etc {
- name: "task_profiles_29.json",
- src: "task_profiles_29.json",
- sub_dir: "task_profiles",
-}
-
-prebuilt_etc {
- name: "task_profiles_30.json",
- src: "task_profiles_30.json",
- sub_dir: "task_profiles",
}
cc_defaults {
@@ -127,6 +77,9 @@ cc_test_host {
"cgroups.recovery.json",
"task_profiles.json",
],
+ test_suites: [
+ "general-tests",
+ ],
}
cc_test {
@@ -151,3 +104,8 @@ cc_test {
"vts",
],
}
+
+vts_config {
+ name: "VtsProcessgroupValidateTest",
+ test_config: "vts_processgroup_validate_test.xml",
+}
diff --git a/libprocessgroup/profiles/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING
new file mode 100644
index 000000000..5ff41123e
--- /dev/null
+++ b/libprocessgroup/profiles/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "libprocessgroup_proto_test",
+ "host": true
+ }
+ ]
+}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 063422091..03419020c 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -15,6 +15,11 @@
"GID": "system"
},
{
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ },
+ {
"Controller": "cpuset",
"Path": "/dev/cpuset",
"Mode": "0755",
@@ -26,23 +31,27 @@
"Path": "/dev/memcg",
"Mode": "0700",
"UID": "root",
- "GID": "system",
- "Optional": true
+ "GID": "system"
+ },
+ {
+ "Controller": "schedtune",
+ "Path": "/dev/stune",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "freezer",
+ "Path": "/dev/freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
}
],
"Cgroups2": {
- "Path": "/sys/fs/cgroup",
- "Mode": "0755",
- "UID": "system",
- "GID": "system",
- "Controllers": [
- {
- "Controller": "freezer",
- "Path": ".",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- }
- ]
+ "Path": "/dev/cg2_bpf",
+ "Mode": "0600",
+ "UID": "root",
+ "GID": "root"
}
}
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index f2de3452a..f4070c550 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,25 +24,19 @@ message Cgroups {
Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
}
-// Next: 8
+// Next: 6
message Cgroup {
string controller = 1 [json_name = "Controller"];
string path = 2 [json_name = "Path"];
string mode = 3 [json_name = "Mode"];
string uid = 4 [json_name = "UID"];
string gid = 5 [json_name = "GID"];
-// Booleans default to false when not specified. File reconstruction fails
-// when a boolean is specified as false, so leave unspecified in that case
-// https://developers.google.com/protocol-buffers/docs/proto3#default
- bool needs_activation = 6 [json_name = "NeedsActivation"];
- bool is_optional = 7 [json_name = "Optional"];
}
-// Next: 6
+// Next: 5
message Cgroups2 {
string path = 1 [json_name = "Path"];
string mode = 2 [json_name = "Mode"];
string uid = 3 [json_name = "UID"];
string gid = 4 [json_name = "GID"];
- repeated Cgroup controllers = 5 [json_name = "Controllers"];
}
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
index e27525262..f0bf5fdf1 100644
--- a/libprocessgroup/profiles/cgroups.recovery.json
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -1,8 +1,9 @@
{
- "Cgroups2": {
- "Path": "/sys/fs/cgroup",
- "Mode": "0755",
- "UID": "root",
- "GID": "root"
- }
+ "Cgroups": [
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ }
+ ]
}
diff --git a/libprocessgroup/profiles/cgroups_28.json b/libprocessgroup/profiles/cgroups_28.json
deleted file mode 100644
index 17d492949..000000000
--- a/libprocessgroup/profiles/cgroups_28.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Cgroups": [
- {
- "Controller": "schedtune",
- "Path": "/dev/stune",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- }
- ]
-}
diff --git a/libprocessgroup/profiles/cgroups_29.json b/libprocessgroup/profiles/cgroups_29.json
deleted file mode 100644
index 17d492949..000000000
--- a/libprocessgroup/profiles/cgroups_29.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Cgroups": [
- {
- "Controller": "schedtune",
- "Path": "/dev/stune",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
- }
- ]
-}
diff --git a/libprocessgroup/profiles/cgroups_30.json b/libprocessgroup/profiles/cgroups_30.json
deleted file mode 100644
index 80a074bf1..000000000
--- a/libprocessgroup/profiles/cgroups_30.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "Cgroups": [
- {
- "Controller": "schedtune",
- "Path": "/dev/stune",
- "Mode": "0755",
- "UID": "system",
- "GID": "system",
- "Optional": true
- }
- ]
-}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 449a50546..bc6bc7c25 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -31,6 +31,16 @@
"File": "memory.swappiness"
},
{
+ "Name": "STuneBoost",
+ "Controller": "schedtune",
+ "File": "schedtune.boost"
+ },
+ {
+ "Name": "STunePreferIdle",
+ "Controller": "schedtune",
+ "File": "schedtune.prefer_idle"
+ },
+ {
"Name": "UClampMin",
"Controller": "cpu",
"File": "cpu.uclamp.min"
@@ -41,14 +51,9 @@
"File": "cpu.uclamp.max"
},
{
- "Name": "UClampLatencySensitive",
- "Controller": "cpu",
- "File": "cpu.uclamp.latency_sensitive"
- },
- {
"Name": "FreezerState",
"Controller": "freezer",
- "File": "cgroup.freeze"
+ "File": "frozen/freezer.state"
}
],
@@ -60,7 +65,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
+ "Controller": "schedtune",
"Path": "background"
}
}
@@ -70,11 +75,11 @@
"Name": "Frozen",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "FreezerState",
- "Value": "1"
+ "Controller": "freezer",
+ "Path": "frozen"
}
}
]
@@ -83,37 +88,24 @@
"Name": "Unfrozen",
"Actions": [
{
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "FreezerState",
- "Value": "0"
- }
- }
- ]
- },
- {
- "Name": "NormalPerformance",
- "Actions": [
- {
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
- "Path": "system"
+ "Controller": "freezer",
+ "Path": ""
}
}
]
},
{
- "Name": "ServicePerformance",
+ "Name": "NormalPerformance",
"Actions": [
{
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
- "Path": "system-background"
+ "Controller": "schedtune",
+ "Path": ""
}
}
]
@@ -125,7 +117,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
+ "Controller": "schedtune",
"Path": "foreground"
}
}
@@ -138,7 +130,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
+ "Controller": "schedtune",
"Path": "top-app"
}
}
@@ -151,7 +143,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
+ "Controller": "schedtune",
"Path": "rt"
}
}
@@ -164,25 +156,12 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "cpu",
+ "Controller": "schedtune",
"Path": "camera-daemon"
}
}
]
},
- {
- "Name": "NNApiHALPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpu",
- "Path": "nnapi-hal"
- }
- }
- ]
- },
{
"Name": "CpuPolicySpread",
@@ -191,7 +170,7 @@
"Name": "SetAttribute",
"Params":
{
- "Name": "UClampLatencySensitive",
+ "Name": "STunePreferIdle",
"Value": "1"
}
}
@@ -204,7 +183,7 @@
"Name": "SetAttribute",
"Params":
{
- "Name": "UClampLatencySensitive",
+ "Name": "STunePreferIdle",
"Value": "0"
}
}
@@ -476,33 +455,6 @@
},
{
- "Name": "SFMainPolicy",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system-background"
- }
- }
- ]
- },
- {
- "Name": "SFRenderEnginePolicy",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "cpuset",
- "Path": "system-background"
- }
- }
- ]
- },
-
- {
"Name": "PerfBoost",
"Actions": [
{
@@ -584,6 +536,32 @@
}
}
]
+ },
+ {
+ "Name": "FreezerThawed",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "THAWED"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerFrozen",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "FROZEN"
+ }
+ }
+ ]
}
],
@@ -605,10 +583,6 @@
"Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
},
{
- "Name": "SCHED_SP_SYSTEM",
- "Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
- },
- {
"Name": "SCHED_SP_RT_APP",
"Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
},
@@ -635,10 +609,6 @@
{
"Name": "CPUSET_SP_RESTRICTED",
"Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
- },
- {
- "Name": "Dex2OatBootComplete",
- "Profiles": [ "SCHED_SP_BACKGROUND" ]
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
deleted file mode 100644
index 9f8378590..000000000
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ /dev/null
@@ -1,135 +0,0 @@
-{
- "Attributes": [
- {
- "Name": "STuneBoost",
- "Controller": "schedtune",
- "File": "schedtune.boost"
- },
- {
- "Name": "STunePreferIdle",
- "Controller": "schedtune",
- "File": "schedtune.prefer_idle"
- }
- ],
-
- "Profiles": [
- {
- "Name": "HighEnergySaving",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "NormalPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "HighPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "foreground"
- }
- }
- ]
- },
- {
- "Name": "MaxPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "top-app"
- }
- }
- ]
- },
- {
- "Name": "RealtimePerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "rt"
- }
- }
- ]
- },
- {
- "Name": "CameraServicePerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "camera-daemon"
- }
- }
- ]
- },
- {
- "Name": "NNApiHALPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "nnapi-hal"
- }
- }
- ]
- },
-
- {
- "Name": "CpuPolicySpread",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "STunePreferIdle",
- "Value": "1"
- }
- }
- ]
- },
- {
- "Name": "CpuPolicyPack",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "STunePreferIdle",
- "Value": "0"
- }
- }
- ]
- }
- ]
-}
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
deleted file mode 100644
index 9f8378590..000000000
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ /dev/null
@@ -1,135 +0,0 @@
-{
- "Attributes": [
- {
- "Name": "STuneBoost",
- "Controller": "schedtune",
- "File": "schedtune.boost"
- },
- {
- "Name": "STunePreferIdle",
- "Controller": "schedtune",
- "File": "schedtune.prefer_idle"
- }
- ],
-
- "Profiles": [
- {
- "Name": "HighEnergySaving",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "NormalPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "HighPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "foreground"
- }
- }
- ]
- },
- {
- "Name": "MaxPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "top-app"
- }
- }
- ]
- },
- {
- "Name": "RealtimePerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "rt"
- }
- }
- ]
- },
- {
- "Name": "CameraServicePerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "camera-daemon"
- }
- }
- ]
- },
- {
- "Name": "NNApiHALPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "nnapi-hal"
- }
- }
- ]
- },
-
- {
- "Name": "CpuPolicySpread",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "STunePreferIdle",
- "Value": "1"
- }
- }
- ]
- },
- {
- "Name": "CpuPolicyPack",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "STunePreferIdle",
- "Value": "0"
- }
- }
- ]
- }
- ]
-}
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
deleted file mode 100644
index 9f8378590..000000000
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ /dev/null
@@ -1,135 +0,0 @@
-{
- "Attributes": [
- {
- "Name": "STuneBoost",
- "Controller": "schedtune",
- "File": "schedtune.boost"
- },
- {
- "Name": "STunePreferIdle",
- "Controller": "schedtune",
- "File": "schedtune.prefer_idle"
- }
- ],
-
- "Profiles": [
- {
- "Name": "HighEnergySaving",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "background"
- }
- }
- ]
- },
- {
- "Name": "NormalPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": ""
- }
- }
- ]
- },
- {
- "Name": "HighPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "foreground"
- }
- }
- ]
- },
- {
- "Name": "MaxPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "top-app"
- }
- }
- ]
- },
- {
- "Name": "RealtimePerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "rt"
- }
- }
- ]
- },
- {
- "Name": "CameraServicePerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "camera-daemon"
- }
- }
- ]
- },
- {
- "Name": "NNApiHALPerformance",
- "Actions": [
- {
- "Name": "JoinCgroup",
- "Params":
- {
- "Controller": "schedtune",
- "Path": "nnapi-hal"
- }
- }
- ]
- },
-
- {
- "Name": "CpuPolicySpread",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "STunePreferIdle",
- "Value": "1"
- }
- }
- ]
- },
- {
- "Name": "CpuPolicyPack",
- "Actions": [
- {
- "Name": "SetAttribute",
- "Params":
- {
- "Name": "STunePreferIdle",
- "Value": "0"
- }
- }
- ]
- }
- ]
-}
diff --git a/libprocessgroup/profiles/vts_processgroup_validate_test.xml b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
new file mode 100644
index 000000000..21d29cdbb
--- /dev/null
+++ b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VtsProcessgroupValidateTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsProcessgroupValidateTest"/>
+ <option name="binary-test-working-directory" value="_32bit::/data/nativetest/" />
+ <option name="binary-test-working-directory" value="_64bit::/data/nativetest64/" />
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="binary-test-disable-framework" value="false"/>
+ <option name="test-timeout" value="30s"/>
+ </test>
+</configuration>
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 1a4196a4d..698e74d40 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -124,8 +124,6 @@ int set_sched_policy(int tid, SchedPolicy policy) {
return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
case SP_TOP_APP:
return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
- case SP_SYSTEM:
- return SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
case SP_RT_APP:
return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
default:
@@ -159,9 +157,10 @@ static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
if (!controller.IsUsable()) return -1;
- if (!controller.GetTaskGroup(tid, &subgroup))
+ if (!controller.GetTaskGroup(tid, &subgroup)) {
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return -1;
-
+ }
return 0;
}
@@ -173,16 +172,11 @@ int get_sched_policy(int tid, SchedPolicy* policy) {
std::string group;
if (schedboost_enabled()) {
if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
- (getCGroupSubsys(tid, "cpu", group) < 0)) {
- LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
- return -1;
- }
+ (getCGroupSubsys(tid, "cpu", group) < 0))
+ return -1;
}
if (group.empty() && cpusets_enabled()) {
- if (getCGroupSubsys(tid, "cpuset", group) < 0) {
- LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
- return -1;
- }
+ if (getCGroupSubsys(tid, "cpuset", group) < 0) return -1;
}
// TODO: replace hardcoded directories
@@ -264,7 +258,7 @@ const char* get_sched_policy_profile_name(SchedPolicy policy) {
*/
static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
"SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
- "SCHED_SP_SYSTEM", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
+ "SCHED_SP_DEFAULT", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
"SCHED_SP_TOP_APP", "SCHED_SP_RT_APP", "SCHED_SP_DEFAULT"};
if (policy < SP_DEFAULT || policy >= SP_CNT) {
return nullptr;
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
index ea6c24706..f6fc066f2 100644
--- a/libprocessgroup/setup/Android.bp
+++ b/libprocessgroup/setup/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_shared {
name: "libprocessgroup_setup",
recovery_available: true,
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
index 699c03cec..f029c4f12 100644
--- a/libprocessgroup/setup/cgroup_descriptor.h
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -25,7 +25,7 @@ namespace cgrouprc {
class CgroupDescriptor {
public:
CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
- mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags);
+ mode_t mode, const std::string& uid, const std::string& gid);
const format::CgroupController* controller() const { return &controller_; }
mode_t mode() const { return mode_; }
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3121d244b..17ea06e09 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -17,7 +17,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "libprocessgroup"
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
@@ -45,7 +44,7 @@
#include "cgroup_descriptor.h"
-using android::base::GetUintProperty;
+using android::base::GetBoolProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -55,126 +54,64 @@ namespace cgrouprc {
static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
-static constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = "/etc/task_profiles/cgroups_%u.json";
-
-static bool ChangeDirModeAndOwner(const std::string& path, mode_t mode, const std::string& uid,
- const std::string& gid, bool permissive_mode = false) {
- uid_t pw_uid = -1;
- gid_t gr_gid = -1;
-
- if (!uid.empty()) {
- passwd* uid_pwd = getpwnam(uid.c_str());
- if (!uid_pwd) {
- PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
- return false;
- }
-
- pw_uid = uid_pwd->pw_uid;
- gr_gid = -1;
-
- if (!gid.empty()) {
- group* gid_pwd = getgrnam(gid.c_str());
- if (!gid_pwd) {
- PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
- return false;
- }
- gr_gid = gid_pwd->gr_gid;
- }
- }
-
- auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
-
- if (dir == NULL) {
- PLOG(ERROR) << "opendir failed for " << path;
- return false;
- }
-
- struct dirent* dir_entry;
- while ((dir_entry = readdir(dir.get()))) {
- if (!strcmp("..", dir_entry->d_name)) {
- continue;
- }
-
- std::string file_path = path + "/" + dir_entry->d_name;
-
- if (pw_uid != -1 && lchown(file_path.c_str(), pw_uid, gr_gid) < 0) {
- PLOG(ERROR) << "lchown() failed for " << file_path;
- return false;
- }
-
- if (fchmodat(AT_FDCWD, file_path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0 &&
- (errno != EROFS || !permissive_mode)) {
- PLOG(ERROR) << "fchmodat() failed for " << path;
- return false;
- }
- }
-
- return true;
-}
-
static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
const std::string& gid) {
- bool permissive_mode = false;
-
if (mode == 0) {
- /* Allow chmod to fail */
- permissive_mode = true;
mode = 0755;
}
if (mkdir(path.c_str(), mode) != 0) {
- // /acct is a special case when the directory already exists
- if (errno != EEXIST) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ // /acct is a special case when the directory already exists
+ // TODO: check if file mode is already what we want instead of using EROFS
+ if (errno != EROFS) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+ } else {
PLOG(ERROR) << "mkdir() failed for " << path;
return false;
- } else {
- permissive_mode = true;
}
}
- if (uid.empty() && permissive_mode) {
+ if (uid.empty()) {
return true;
}
- if (!ChangeDirModeAndOwner(path, mode, uid, gid, permissive_mode)) {
- PLOG(ERROR) << "change of ownership or mode failed for " << path;
+ passwd* uid_pwd = getpwnam(uid.c_str());
+ if (!uid_pwd) {
+ PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
return false;
}
- return true;
-}
-
-static void MergeCgroupToDescriptors(std::map<std::string, CgroupDescriptor>* descriptors,
- const Json::Value& cgroup, const std::string& name,
- const std::string& root_path, int cgroups_version) {
- std::string path;
-
- if (!root_path.empty()) {
- path = root_path + "/" + cgroup["Path"].asString();
- } else {
- path = cgroup["Path"].asString();
+ uid_t pw_uid = uid_pwd->pw_uid;
+ gid_t gr_gid = -1;
+ if (!gid.empty()) {
+ group* gid_pwd = getgrnam(gid.c_str());
+ if (!gid_pwd) {
+ PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+ return false;
+ }
+ gr_gid = gid_pwd->gr_gid;
}
- uint32_t controller_flags = 0;
-
- if (cgroup["NeedsActivation"].isBool() && cgroup["NeedsActivation"].asBool()) {
- controller_flags |= CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION;
+ if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+ PLOG(ERROR) << "lchown() failed for " << path;
+ return false;
}
- if (cgroup["Optional"].isBool() && cgroup["Optional"].asBool()) {
- controller_flags |= CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
+ /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+ if (mode & (S_ISUID | S_ISGID)) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
}
- CgroupDescriptor descriptor(
- cgroups_version, name, path, std::strtoul(cgroup["Mode"].asString().c_str(), 0, 8),
- cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags);
-
- auto iter = descriptors->find(name);
- if (iter == descriptors->end()) {
- descriptors->emplace(name, descriptor);
- } else {
- iter->second = descriptor;
- }
+ return true;
}
static bool ReadDescriptorsFromFile(const std::string& file_name,
@@ -187,12 +124,10 @@ static bool ReadDescriptorsFromFile(const std::string& file_name,
return false;
}
- Json::CharReaderBuilder builder;
- std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ Json::Reader reader;
Json::Value root;
- std::string errorMessage;
- if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
- LOG(ERROR) << "Failed to parse cgroups description: " << errorMessage;
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
return false;
}
@@ -200,19 +135,36 @@ static bool ReadDescriptorsFromFile(const std::string& file_name,
const Json::Value& cgroups = root["Cgroups"];
for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
std::string name = cgroups[i]["Controller"].asString();
- MergeCgroupToDescriptors(descriptors, cgroups[i], name, "", 1);
+ auto iter = descriptors->find(name);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(
+ name, CgroupDescriptor(
+ 1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+ } else {
+ iter->second = CgroupDescriptor(
+ 1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+ }
}
}
if (root.isMember("Cgroups2")) {
const Json::Value& cgroups2 = root["Cgroups2"];
- std::string root_path = cgroups2["Path"].asString();
- MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_CONTROLLER_NAME, "", 2);
-
- const Json::Value& childGroups = cgroups2["Controllers"];
- for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {
- std::string name = childGroups[i]["Controller"].asString();
- MergeCgroupToDescriptors(descriptors, childGroups[i], name, root_path, 2);
+ auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(
+ CGROUPV2_CONTROLLER_NAME,
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+ } else {
+ iter->second =
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString());
}
}
@@ -225,18 +177,6 @@ static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors
return false;
}
- // load API-level specific system cgroups descriptors if available
- unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
- if (api_level > 0) {
- std::string api_cgroups_path =
- android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level);
- if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) {
- if (!ReadDescriptorsFromFile(api_cgroups_path, descriptors)) {
- return false;
- }
- }
- }
-
// load vendor cgroup descriptors if the file exists
if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
!ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
@@ -252,49 +192,17 @@ static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors
static bool SetupCgroup(const CgroupDescriptor& descriptor) {
const format::CgroupController* controller = descriptor.controller();
+ // mkdir <path> [mode] [owner] [group]
+ if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+ LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+ return false;
+ }
+
int result;
if (controller->version() == 2) {
- result = 0;
- if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
- // /sys/fs/cgroup is created by cgroup2 with specific selinux permissions,
- // try to create again in case the mount point is changed
- if (!Mkdir(controller->path(), 0, "", "")) {
- LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
- return false;
- }
-
- result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- nullptr);
-
- // selinux permissions change after mounting, so it's ok to change mode and owner now
- if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
- descriptor.gid())) {
- LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
- result = -1;
- }
- } else {
- if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
- LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
- return false;
- }
-
- if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
- std::string str = std::string("+") + controller->name();
- std::string path = std::string(controller->path()) + "/cgroup.subtree_control";
-
- if (!base::WriteStringToFile(str, path)) {
- LOG(ERROR) << "Failed to activate controller " << controller->name();
- return false;
- }
- }
- }
+ result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ nullptr);
} else {
- // mkdir <path> [mode] [owner] [group]
- if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
- LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
- return false;
- }
-
// Unfortunately historically cpuset controller was mounted using a mount command
// different from all other controllers. This results in controller attributes not
// to be prepended with controller name. For example this way instead of
@@ -312,15 +220,8 @@ static bool SetupCgroup(const CgroupDescriptor& descriptor) {
}
if (result < 0) {
- bool optional = controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
-
- if (optional && errno == EINVAL) {
- // Optional controllers are allowed to fail to mount if kernel does not support them
- LOG(INFO) << "Optional " << controller->name() << " cgroup controller is not mounted";
- } else {
- PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
- return false;
- }
+ PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+ return false;
}
return true;
@@ -366,8 +267,8 @@ static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descripto
CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
const std::string& path, mode_t mode, const std::string& uid,
- const std::string& gid, uint32_t flags = 0)
- : controller_(version, flags, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+ const std::string& gid)
+ : controller_(version, 0, name, path), mode_(mode), uid_(uid), gid_(gid) {}
void CgroupDescriptor::set_mounted(bool mounted) {
uint32_t flags = controller_.flags();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index cf74e6557..a638fcac6 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -23,9 +23,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
#include <android-base/threads.h>
#include <cutils/android_filesystem_config.h>
@@ -39,17 +37,12 @@
#endif
using android::base::GetThreadId;
-using android::base::GetUintProperty;
using android::base::StringPrintf;
-using android::base::StringReplace;
using android::base::unique_fd;
using android::base::WriteStringToFile;
-static constexpr const char* TASK_PROFILE_DB_FILE = "/etc/task_profiles.json";
-static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_profiles.json";
-
-static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
- "/etc/task_profiles/task_profiles_%u.json";
+#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
+#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
controller_ = controller;
@@ -264,39 +257,6 @@ bool SetCgroupAction::ExecuteForTask(int tid) const {
return true;
}
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
- std::string filepath(filepath_), value(value_);
-
- filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
- filepath = StringReplace(filepath, "<pid>", std::to_string(pid), true);
- value = StringReplace(value, "<uid>", std::to_string(uid), true);
- value = StringReplace(value, "<pid>", std::to_string(pid), true);
-
- if (!WriteStringToFile(value, filepath)) {
- if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
- return false;
- }
-
- return true;
-}
-
-bool WriteFileAction::ExecuteForTask(int tid) const {
- std::string filepath(filepath_), value(value_);
- int uid = getuid();
-
- filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
- filepath = StringReplace(filepath, "<pid>", std::to_string(tid), true);
- value = StringReplace(value, "<uid>", std::to_string(uid), true);
- value = StringReplace(value, "<pid>", std::to_string(tid), true);
-
- if (!WriteStringToFile(value, filepath)) {
- if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
- return false;
- }
-
- return true;
-}
-
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
if (!profile->ExecuteForProcess(uid, pid)) {
@@ -308,7 +268,9 @@ bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
bool ApplyProfileAction::ExecuteForTask(int tid) const {
for (const auto& profile : profiles_) {
- profile->ExecuteForTask(tid);
+ if (!profile->ExecuteForTask(tid)) {
+ PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
+ }
}
return true;
}
@@ -394,19 +356,6 @@ TaskProfiles::TaskProfiles() {
LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
}
- // load API-level specific system task profiles if available
- unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
- if (api_level > 0) {
- std::string api_profiles_path =
- android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
- if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
- if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
- LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
- << "] failed";
- }
- }
- }
-
// load vendor task profiles if the file exists
if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
@@ -423,12 +372,10 @@ bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
return false;
}
- Json::CharReaderBuilder builder;
- std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ Json::Reader reader;
Json::Value root;
- std::string errorMessage;
- if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
- LOG(ERROR) << "Failed to parse task profiles: " << errorMessage;
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse task profiles: " << reader.getFormattedErrorMessages();
return false;
}
@@ -512,21 +459,6 @@ bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
} else {
LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
}
- } else if (action_name == "WriteFile") {
- std::string attr_filepath = params_val["FilePath"].asString();
- std::string attr_value = params_val["Value"].asString();
- if (!attr_filepath.empty() && !attr_value.empty()) {
- std::string attr_logfailures = params_val["LogFailures"].asString();
- bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
- profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
- logfailures));
- } else if (attr_filepath.empty()) {
- LOG(WARNING) << "WriteFile: invalid parameter: "
- << "empty filepath";
- } else if (attr_value.empty()) {
- LOG(WARNING) << "WriteFile: invalid parameter: "
- << "empty value";
- }
} else {
LOG(WARNING) << "Unknown profile action: " << action_name;
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 25a84b0c1..2983a09b3 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -139,21 +139,6 @@ class SetCgroupAction : public ProfileAction {
bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
};
-// Write to file action
-class WriteFileAction : public ProfileAction {
- public:
- WriteFileAction(const std::string& filepath, const std::string& value,
- bool logfailures) noexcept
- : filepath_(filepath), value_(value), logfailures_(logfailures) {}
-
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
-
- private:
- std::string filepath_, value_;
- bool logfailures_;
-};
-
class TaskProfile {
public:
TaskProfile() : res_cached_(false) {}
diff --git a/libprocinfo/.clang-format b/libprocinfo/.clang-format
new file mode 120000
index 000000000..fd0645fdf
--- /dev/null
+++ b/libprocinfo/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2 \ No newline at end of file
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
new file mode 100644
index 000000000..ae4574276
--- /dev/null
+++ b/libprocinfo/Android.bp
@@ -0,0 +1,131 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libprocinfo_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+
+cc_library {
+ name: "libprocinfo",
+ defaults: ["libprocinfo_defaults"],
+ vendor_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+ recovery_available: true,
+ vndk: {
+ enabled: true,
+ },
+ host_supported: true,
+ srcs: [
+ "process.cpp",
+ "process_map.cpp",
+ ],
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: ["libbase"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+ name: "libprocinfo_test",
+ defaults: ["libprocinfo_defaults"],
+ host_supported: true,
+ isolated: true,
+ srcs: [
+ "process_test.cpp",
+ "process_map_test.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+
+ shared_libs: [
+ "libbase",
+ "libprocinfo",
+ ],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ data: [
+ "testdata/*",
+ ],
+
+ test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+ name: "libprocinfo_benchmark",
+ defaults: ["libprocinfo_defaults"],
+ srcs: [
+ "process_map_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libprocinfo",
+ "libunwindstack",
+ ],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ data: [
+ "testdata/*",
+ ],
+}
diff --git a/libprocinfo/OWNERS b/libprocinfo/OWNERS
new file mode 100644
index 000000000..a70cc57ef
--- /dev/null
+++ b/libprocinfo/OWNERS
@@ -0,0 +1 @@
+jmgao@google.com
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
new file mode 100644
index 000000000..9278e1819
--- /dev/null
+++ b/libprocinfo/include/procinfo/process.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace procinfo {
+
+#if defined(__linux__)
+
+enum ProcessState {
+ kProcessStateUnknown,
+ kProcessStateRunning,
+ kProcessStateSleeping,
+ kProcessStateUninterruptibleWait,
+ kProcessStateStopped,
+ kProcessStateZombie,
+};
+
+struct ProcessInfo {
+ std::string name;
+ ProcessState state;
+ pid_t tid;
+ pid_t pid;
+ pid_t ppid;
+ pid_t tracer;
+ uid_t uid;
+ uid_t gid;
+};
+
+// Parse the contents of /proc/<tid>/status into |process_info|.
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error = nullptr);
+
+// Parse the contents of <fd>/status into |process_info|.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error = nullptr);
+
+// Fetch the list of threads from a given process's /proc/<pid> directory.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+template <typename Collection>
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out, std::string* error = nullptr) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ out->clear();
+
+ int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
+ if (!dir) {
+ if (error != nullptr) {
+ *error = "failed to open task directory";
+ }
+ return false;
+ }
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
+ pid_t tid;
+ if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
+ if (error != nullptr) {
+ *error = std::string("failed to parse task id: ") + dent->d_name;
+ }
+ return false;
+ }
+
+ out->insert(out->end(), tid);
+ }
+ }
+
+ return true;
+}
+
+template <typename Collection>
+auto GetProcessTids(pid_t pid, Collection* out, std::string* error = nullptr) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ char task_path[PATH_MAX];
+ if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
+ if (error != nullptr) {
+ *error = "task path overflow (pid = " + std::to_string(pid) + ")";
+ }
+ return false;
+ }
+
+ android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ if (error != nullptr) {
+ *error = std::string("failed to open ") + task_path;
+ }
+ return false;
+ }
+
+ return GetProcessTidsFromProcPidFd(fd.get(), out, error);
+}
+
+#endif
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
new file mode 100644
index 000000000..569a02221
--- /dev/null
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+
+namespace android {
+namespace procinfo {
+
+template <class CallbackType>
+bool ReadMapFileContent(char* content, const CallbackType& callback) {
+ uint64_t start_addr;
+ uint64_t end_addr;
+ uint16_t flags;
+ uint64_t pgoff;
+ ino_t inode;
+ char* next_line = content;
+ char* p;
+
+ auto pass_space = [&]() {
+ if (*p != ' ') {
+ return false;
+ }
+ while (*p == ' ') {
+ p++;
+ }
+ return true;
+ };
+
+ auto pass_xdigit = [&]() {
+ if (!isxdigit(*p)) {
+ return false;
+ }
+ do {
+ p++;
+ } while (isxdigit(*p));
+ return true;
+ };
+
+ while (next_line != nullptr && *next_line != '\0') {
+ p = next_line;
+ next_line = strchr(next_line, '\n');
+ if (next_line != nullptr) {
+ *next_line = '\0';
+ next_line++;
+ }
+ // Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
+ char* end;
+ // start_addr
+ start_addr = strtoull(p, &end, 16);
+ if (end == p || *end != '-') {
+ return false;
+ }
+ p = end + 1;
+ // end_addr
+ end_addr = strtoull(p, &end, 16);
+ if (end == p) {
+ return false;
+ }
+ p = end;
+ if (!pass_space()) {
+ return false;
+ }
+ // flags
+ flags = 0;
+ if (*p == 'r') {
+ flags |= PROT_READ;
+ } else if (*p != '-') {
+ return false;
+ }
+ p++;
+ if (*p == 'w') {
+ flags |= PROT_WRITE;
+ } else if (*p != '-') {
+ return false;
+ }
+ p++;
+ if (*p == 'x') {
+ flags |= PROT_EXEC;
+ } else if (*p != '-') {
+ return false;
+ }
+ p++;
+ if (*p != 'p' && *p != 's') {
+ return false;
+ }
+ p++;
+ if (!pass_space()) {
+ return false;
+ }
+ // pgoff
+ pgoff = strtoull(p, &end, 16);
+ if (end == p) {
+ return false;
+ }
+ p = end;
+ if (!pass_space()) {
+ return false;
+ }
+ // major:minor
+ if (!pass_xdigit() || *p++ != ':' || !pass_xdigit() || !pass_space()) {
+ return false;
+ }
+ // inode
+ inode = strtoull(p, &end, 10);
+ if (end == p) {
+ return false;
+ }
+ p = end;
+
+ if (*p != '\0' && !pass_space()) {
+ return false;
+ }
+
+ // filename
+ callback(start_addr, end_addr, flags, pgoff, inode, p);
+ }
+ return true;
+}
+
+inline bool ReadMapFile(const std::string& map_file,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
+ std::string content;
+ if (!android::base::ReadFileToString(map_file, &content)) {
+ return false;
+ }
+ return ReadMapFileContent(&content[0], callback);
+}
+
+inline bool ReadProcessMaps(pid_t pid,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
+ return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
+}
+
+struct MapInfo {
+ uint64_t start;
+ uint64_t end;
+ uint16_t flags;
+ uint64_t pgoff;
+ ino_t inode;
+ std::string name;
+
+ MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
+};
+
+inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
+ return ReadProcessMaps(
+ pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
+}
+
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback);
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
new file mode 100644
index 000000000..2efd49c37
--- /dev/null
+++ b/libprocinfo/process.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace procinfo {
+
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d", tid);
+
+ unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
+ if (dirfd == -1) {
+ if (error != nullptr) {
+ *error = std::string("failed to open ") + path;
+ }
+ return false;
+ }
+
+ return GetProcessInfoFromProcPidFd(dirfd.get(), process_info, error);
+}
+
+static ProcessState parse_state(const char* state) {
+ switch (*state) {
+ case 'R':
+ return kProcessStateRunning;
+ case 'S':
+ return kProcessStateSleeping;
+ case 'D':
+ return kProcessStateUninterruptibleWait;
+ case 'T':
+ return kProcessStateStopped;
+ case 'Z':
+ return kProcessStateZombie;
+ default:
+ return kProcessStateUnknown;
+ }
+}
+
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error) {
+ int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
+
+ if (status_fd == -1) {
+ if (error != nullptr) {
+ *error = "failed to open status fd in GetProcessInfoFromProcPidFd";
+ }
+ return false;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
+ if (!fp) {
+ if (error != nullptr) {
+ *error = "failed to open status file in GetProcessInfoFromProcPidFd";
+ }
+ close(status_fd);
+ return false;
+ }
+
+ int field_bitmap = 0;
+ static constexpr int finished_bitmap = 255;
+ char* line = nullptr;
+ size_t len = 0;
+
+ while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
+ char* tab = strchr(line, '\t');
+ if (tab == nullptr) {
+ continue;
+ }
+
+ size_t header_len = tab - line;
+ std::string header = std::string(line, header_len);
+ if (header == "Name:") {
+ std::string name = line + header_len + 1;
+
+ // line includes the trailing newline.
+ name.pop_back();
+ process_info->name = std::move(name);
+
+ field_bitmap |= 1;
+ } else if (header == "Pid:") {
+ process_info->tid = atoi(tab + 1);
+ field_bitmap |= 2;
+ } else if (header == "Tgid:") {
+ process_info->pid = atoi(tab + 1);
+ field_bitmap |= 4;
+ } else if (header == "PPid:") {
+ process_info->ppid = atoi(tab + 1);
+ field_bitmap |= 8;
+ } else if (header == "TracerPid:") {
+ process_info->tracer = atoi(tab + 1);
+ field_bitmap |= 16;
+ } else if (header == "Uid:") {
+ process_info->uid = atoi(tab + 1);
+ field_bitmap |= 32;
+ } else if (header == "Gid:") {
+ process_info->gid = atoi(tab + 1);
+ field_bitmap |= 64;
+ } else if (header == "State:") {
+ process_info->state = parse_state(tab + 1);
+ field_bitmap |= 128;
+ }
+ }
+
+ free(line);
+ return field_bitmap == finished_bitmap;
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map.cpp b/libprocinfo/process_map.cpp
new file mode 100644
index 000000000..5e240b99f
--- /dev/null
+++ b/libprocinfo/process_map.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <procinfo/process.h>
+
+namespace android {
+namespace procinfo {
+
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
+ if (buffer == nullptr || buffer_size == 0) {
+ return false;
+ }
+
+ int fd = open(map_file, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ char* char_buffer = reinterpret_cast<char*>(buffer);
+ size_t start = 0;
+ size_t read_bytes = 0;
+ char* line = nullptr;
+ bool read_complete = false;
+ while (true) {
+ ssize_t bytes =
+ TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
+ if (bytes <= 0) {
+ if (read_bytes == 0) {
+ close(fd);
+ return bytes == 0;
+ }
+ // Treat the last piece of data as the last line.
+ char_buffer[start + read_bytes] = '\n';
+ bytes = 1;
+ read_complete = true;
+ }
+ read_bytes += bytes;
+
+ while (read_bytes > 0) {
+ char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
+ if (newline == nullptr) {
+ break;
+ }
+ *newline = '\0';
+ line = &char_buffer[start];
+ start = newline - char_buffer + 1;
+ read_bytes -= newline - line + 1;
+
+ // Ignore the return code, errors are okay.
+ ReadMapFileContent(line, callback);
+ }
+
+ if (read_complete) {
+ close(fd);
+ return true;
+ }
+
+ if (start == 0 && read_bytes == buffer_size - 1) {
+ // The buffer provided is too small to contain this line, give up
+ // and indicate failure.
+ close(fd);
+ return false;
+ }
+
+ // Copy any leftover data to the front of the buffer.
+ if (start > 0) {
+ if (read_bytes > 0) {
+ memmove(char_buffer, &char_buffer[start], read_bytes);
+ }
+ start = 0;
+ }
+ }
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
new file mode 100644
index 000000000..eba4fd0d8
--- /dev/null
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Maps.h>
+
+#include <benchmark/benchmark.h>
+
+static void BM_ReadMapFile(benchmark::State& state) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ for (auto _ : state) {
+ std::vector<android::procinfo::MapInfo> maps;
+ android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
+ uint64_t pgoff, ino_t inode, const char* name) {
+ maps.emplace_back(start, end, flags, pgoff, inode, name);
+ });
+ CHECK_EQ(maps.size(), 2043u);
+ }
+}
+BENCHMARK(BM_ReadMapFile);
+
+static void BM_unwindstack_FileMaps(benchmark::State& state) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ for (auto _ : state) {
+ unwindstack::FileMaps maps(map_file);
+ maps.Parse();
+ CHECK_EQ(maps.Total(), 2043u);
+ }
+}
+BENCHMARK(BM_unwindstack_FileMaps);
+
+static void BM_unwindstack_BufferMaps(benchmark::State& state) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ std::string content;
+ CHECK(android::base::ReadFileToString(map_file, &content));
+ for (auto _ : state) {
+ unwindstack::BufferMaps maps(content.c_str());
+ maps.Parse();
+ CHECK_EQ(maps.Total(), 2043u);
+ }
+}
+BENCHMARK(BM_unwindstack_BufferMaps);
+
+static void BM_backtrace_BacktraceMap(benchmark::State& state) {
+ pid_t pid = getpid();
+ for (auto _ : state) {
+ BacktraceMap* map = BacktraceMap::Create(pid, true);
+ CHECK(map != nullptr);
+ delete map;
+ }
+}
+BENCHMARK(BM_backtrace_BacktraceMap);
+
+BENCHMARK_MAIN();
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
new file mode 100644
index 000000000..b1bdc08a5
--- /dev/null
+++ b/libprocinfo/process_map_test.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include <gtest/gtest.h>
+
+TEST(process_map, ReadMapFile) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ std::vector<android::procinfo::MapInfo> maps;
+ ASSERT_TRUE(android::procinfo::ReadMapFile(
+ map_file,
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
+ ASSERT_EQ(2043u, maps.size());
+ ASSERT_EQ(maps[0].start, 0x12c00000ULL);
+ ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
+ ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
+ ASSERT_EQ(maps[0].pgoff, 0ULL);
+ ASSERT_EQ(maps[0].inode, 10267643UL);
+ ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
+ ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
+ ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
+ ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
+ ASSERT_EQ(maps[876].pgoff, 0ULL);
+ ASSERT_EQ(maps[876].inode, 2407UL);
+ ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
+ ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
+ ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
+ ASSERT_EQ(maps[1260].flags, PROT_READ);
+ ASSERT_EQ(maps[1260].pgoff, 0ULL);
+ ASSERT_EQ(maps[1260].inode, 10266154UL);
+ ASSERT_EQ(maps[1260].name,
+ "[anon:dalvik-classes.dex extracted in memory from "
+ "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
+}
+
+TEST(process_map, ReadProcessMaps) {
+ std::vector<android::procinfo::MapInfo> maps;
+ ASSERT_TRUE(android::procinfo::ReadProcessMaps(
+ getpid(),
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
+ ASSERT_GT(maps.size(), 0u);
+ maps.clear();
+ ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
+ ASSERT_GT(maps.size(), 0u);
+}
+
+extern "C" void malloc_disable();
+extern "C" void malloc_enable();
+
+struct TestMapInfo {
+ TestMapInfo() = default;
+ TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* new_name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
+ strcpy(name, new_name);
+ }
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint16_t flags = 0;
+ uint64_t pgoff = 0;
+ ino_t inode = 0;
+ char name[100] = {};
+};
+
+void VerifyReadMapFileAsyncSafe(const char* maps_data,
+ const std::vector<TestMapInfo>& expected_info) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
+
+ std::vector<TestMapInfo> saved_info(expected_info.size());
+ size_t num_maps = 0;
+
+ auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) {
+ if (num_maps != saved_info.size()) {
+ TestMapInfo& saved = saved_info[num_maps];
+ saved.start = start;
+ saved.end = end;
+ saved.flags = flags;
+ saved.pgoff = pgoff;
+ saved.inode = inode;
+ strcpy(saved.name, name);
+ }
+ num_maps++;
+ };
+
+ std::vector<char> buffer(64 * 1024);
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ bool parsed =
+ android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
+ ASSERT_EQ(expected_info.size(), num_maps);
+ for (size_t i = 0; i < expected_info.size(); i++) {
+ const TestMapInfo& expected = expected_info[i];
+ const TestMapInfo& saved = saved_info[i];
+ EXPECT_EQ(expected.start, saved.start);
+ EXPECT_EQ(expected.end, saved.end);
+ EXPECT_EQ(expected.flags, saved.flags);
+ EXPECT_EQ(expected.pgoff, saved.pgoff);
+ EXPECT_EQ(expected.inode, saved.inode);
+ EXPECT_STREQ(expected.name, saved.name);
+ }
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_invalid) {
+ std::vector<TestMapInfo> expected_info;
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+ "/lib/fake.so");
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
+ expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+ "/lib/fake.so");
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
+ expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
+
+ VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
+ expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
+ expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
+ expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
+
+ std::string map_data =
+ "0a0000-0c0000 rwxp 00000001 00:05 100\n"
+ "0d0000-0e0000 r--p 00000002 00:05 101 /lib/libsomething1.so\n"
+ "0f0000-100000 -w-p 00000003 00:05 102 /lib/libsomething2.so\n"
+ "110000-120000 --xp 00000004 00:05 103 [anon:something or another]\n";
+
+ VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
+ std::vector<TestMapInfo> expected_info;
+ std::string map_data;
+ uint64_t start = 0xa0000;
+ for (size_t i = 0; i < 10000; i++) {
+ map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
+ start, start + 0x1000, i, 1000 + i);
+ expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
+ }
+
+ VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[10];
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[10];
+ bool parsed =
+ android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFd(
+ "0a0000-0c0000 rwxp 00000001 00:05 100 /fake/lib.so\n", tf.fd));
+
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[39];
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
new file mode 100644
index 000000000..9da927899
--- /dev/null
+++ b/libprocinfo/process_test.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+using namespace std::chrono_literals;
+
+#if !defined(__BIONIC__)
+#include <syscall.h>
+static pid_t gettid() {
+ return syscall(__NR_gettid);
+}
+#endif
+
+TEST(process_info, process_info_smoke) {
+ android::procinfo::ProcessInfo self;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self));
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+}
+
+TEST(process_info, process_info_proc_pid_fd_smoke) {
+ android::procinfo::ProcessInfo self;
+ int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self));
+
+ // Process name is capped at 15 bytes.
+ ASSERT_EQ("libprocinfo_tes", self.name);
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+ close(fd);
+}
+
+TEST(process_info, process_tids_smoke) {
+ pid_t main_tid = gettid();
+ std::thread([main_tid]() {
+ pid_t thread_tid = gettid();
+
+ {
+ std::vector<pid_t> vec;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid));
+ }
+
+ {
+ std::set<pid_t> set;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid));
+ }
+ }).join();
+}
+
+TEST(process_info, process_state) {
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+ pid_t forkpid = fork();
+
+ ASSERT_NE(-1, forkpid);
+ if (forkpid == 0) {
+ close(pipefd[1]);
+ char buf;
+ TEMP_FAILURE_RETRY(read(pipefd[0], &buf, 1));
+ _exit(0);
+ }
+
+ // Give the child some time to get to the read.
+ std::this_thread::sleep_for(100ms);
+
+ android::procinfo::ProcessInfo procinfo;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ ASSERT_EQ(android::procinfo::kProcessStateSleeping, procinfo.state);
+
+ ASSERT_EQ(0, kill(forkpid, SIGKILL));
+
+ // Give the kernel some time to kill the child.
+ std::this_thread::sleep_for(100ms);
+
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(forkpid, &procinfo));
+ ASSERT_EQ(android::procinfo::kProcessStateZombie, procinfo.state);
+
+ ASSERT_EQ(forkpid, waitpid(forkpid, nullptr, 0));
+}
diff --git a/libprocinfo/testdata/maps b/libprocinfo/testdata/maps
new file mode 100644
index 000000000..098cf257b
--- /dev/null
+++ b/libprocinfo/testdata/maps
@@ -0,0 +1,2043 @@
+12c00000-2ac00000 rw-p 00000000 00:05 10267643 [anon:dalvik-main space (region space)]
+6fb5d000-6fd6e000 rw-p 00000000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+6fd6e000-6fd82000 r--p 00211000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+6fd82000-6fe47000 rw-p 00000000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+6fe47000-6fe52000 r--p 000c5000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+6fe52000-6fe84000 rw-p 00000000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+6fe84000-6fe87000 r--p 00032000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+6fe87000-6feb2000 rw-p 00000000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+6feb2000-6feb5000 r--p 0002b000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+6feb5000-6fef4000 rw-p 00000000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+6fef4000-6fefb000 r--p 0003f000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+6fefb000-6ff3f000 rw-p 00000000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+6ff3f000-6ff45000 r--p 00044000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+6ff45000-6ff7a000 rw-p 00000000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+6ff7a000-6ff85000 r--p 00035000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+6ff85000-70594000 rw-p 00000000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70594000-705cb000 r--p 0060f000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+705cb000-7061f000 rw-p 00000000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+7061f000-70629000 r--p 00054000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70629000-70635000 rw-p 00000000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70635000-70636000 r--p 0000c000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70636000-70644000 rw-p 00000000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70644000-70645000 r--p 0000e000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70645000-70648000 rw-p 00000000 103:1d 639544 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.base-V1.0-java.art
+70648000-7064c000 rw-p 00000000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7064c000-7064d000 r--p 00004000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7064d000-7064e000 rw-p 00000000 103:1d 639550 /data/dalvik-cache/arm64/system@framework@boot-framework-oahl-backward-compatibility.art
+7064e000-70652000 rw-p 00000000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70652000-70653000 r--p 00004000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70653000-70654000 rw-p 00000000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70654000-70655000 r--p 00001000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70655000-70731000 r--p 00000000 fc:00 940 /system/framework/arm64/boot.oat
+70731000-709ca000 r-xp 000dc000 fc:00 940 /system/framework/arm64/boot.oat
+709ca000-709cb000 rw-p 00000000 00:00 0 [anon:.bss]
+709cb000-70e4c000 r--s 00000000 fc:00 961 /system/framework/boot.vdex
+70e4c000-70e4d000 r--p 00375000 fc:00 940 /system/framework/arm64/boot.oat
+70e4d000-70e4e000 rw-p 00376000 fc:00 940 /system/framework/arm64/boot.oat
+70e4e000-70eab000 r--p 00000000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+70eab000-70fad000 r-xp 0005d000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+70fad000-70fae000 rw-p 00000000 00:00 0 [anon:.bss]
+70fae000-712a9000 r--s 00000000 fc:00 702 /system/framework/boot-core-libart.vdex
+712a9000-712aa000 r--p 0015f000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+712aa000-712ab000 rw-p 00160000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+712ab000-712bb000 r--p 00000000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+712bb000-712e4000 r-xp 00010000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+712e4000-712e5000 rw-p 00000000 00:00 0 [anon:.bss]
+712e5000-71346000 r--s 00000000 fc:00 970 /system/framework/boot-conscrypt.vdex
+71346000-71347000 r--p 00039000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+71347000-71348000 rw-p 0003a000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+71348000-71361000 r--p 00000000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+71361000-713a3000 r-xp 00019000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+713a3000-713a4000 rw-p 00000000 00:00 0 [anon:.bss]
+713a4000-71403000 r--s 00000000 fc:00 886 /system/framework/boot-okhttp.vdex
+71403000-71404000 r--p 0005b000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+71404000-71405000 rw-p 0005c000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+71405000-71415000 r--p 00000000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+71415000-71437000 r-xp 00010000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+71437000-71438000 rw-p 00000000 00:00 0 [anon:.bss]
+71438000-7157b000 r--s 00000000 fc:00 1006 /system/framework/boot-bouncycastle.vdex
+7157b000-7157c000 r--p 00032000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+7157c000-7157d000 rw-p 00033000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+7157d000-71583000 r--p 00000000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+71583000-71584000 r-xp 00006000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+71584000-716a7000 r--s 00000000 fc:00 883 /system/framework/boot-apache-xml.vdex
+716a7000-716a8000 r--p 00007000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+716a8000-716a9000 rw-p 00008000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+716a9000-716b5000 r--p 00000000 fc:00 891 /system/framework/arm64/boot-ext.oat
+716b5000-716cc000 r-xp 0000c000 fc:00 891 /system/framework/arm64/boot-ext.oat
+716cc000-716cd000 rw-p 00000000 00:00 0 [anon:.bss]
+716cd000-717b8000 r--s 00000000 fc:00 879 /system/framework/boot-ext.vdex
+717b8000-717b9000 r--p 00023000 fc:00 891 /system/framework/arm64/boot-ext.oat
+717b9000-717ba000 rw-p 00024000 fc:00 891 /system/framework/arm64/boot-ext.oat
+717ba000-71aeb000 r--p 00000000 fc:00 943 /system/framework/arm64/boot-framework.oat
+71aeb000-72390000 r-xp 00331000 fc:00 943 /system/framework/arm64/boot-framework.oat
+72390000-72396000 rw-p 00000000 00:00 0 [anon:.bss]
+72396000-73746000 r--s 00000000 fc:00 985 /system/framework/boot-framework.vdex
+73746000-73747000 r--p 00bd6000 fc:00 943 /system/framework/arm64/boot-framework.oat
+73747000-73748000 rw-p 00bd7000 fc:00 943 /system/framework/arm64/boot-framework.oat
+73748000-73780000 r--p 00000000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73780000-73818000 r-xp 00038000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73818000-7381a000 rw-p 00000000 00:00 0 [anon:.bss]
+7381a000-73af0000 r--s 00000000 fc:00 697 /system/framework/boot-telephony-common.vdex
+73af0000-73af1000 r--p 000d0000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73af1000-73af2000 rw-p 000d1000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73af2000-73af6000 r--p 00000000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73af6000-73af8000 r-xp 00004000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73af8000-73af9000 rw-p 00000000 00:00 0 [anon:.bss]
+73af9000-73b1e000 r--s 00000000 fc:00 959 /system/framework/boot-voip-common.vdex
+73b1e000-73b1f000 r--p 00006000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73b1f000-73b20000 rw-p 00007000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73b20000-73b23000 r--p 00000000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b23000-73b25000 r-xp 00003000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b25000-73b26000 rw-p 00000000 00:00 0 [anon:.bss]
+73b26000-73b48000 r--s 00000000 fc:00 957 /system/framework/boot-ims-common.vdex
+73b48000-73b49000 r--p 00005000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b49000-73b4a000 rw-p 00006000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b4a000-73b4d000 r--p 00000000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b4d000-73b4e000 r-xp 00003000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b4e000-73b55000 r--s 00000000 fc:00 972 /system/framework/boot-android.hidl.base-V1.0-java.vdex
+73b55000-73b56000 r--p 00004000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b56000-73b57000 rw-p 00005000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b57000-73b5a000 r--p 00000000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b5a000-73b5c000 r-xp 00003000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b5c000-73b5d000 rw-p 00000000 00:00 0 [anon:.bss]
+73b5d000-73b68000 r--s 00000000 fc:00 704 /system/framework/boot-android.hidl.manager-V1.0-java.vdex
+73b68000-73b69000 r--p 00005000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b69000-73b6a000 rw-p 00006000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b6a000-73b6d000 r--p 00000000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b6d000-73b6e000 r-xp 00003000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b6e000-73b6f000 r--s 00000000 fc:00 994 /system/framework/boot-framework-oahl-backward-compatibility.vdex
+73b6f000-73b70000 r--p 00004000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b70000-73b71000 rw-p 00005000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b71000-73b75000 r--p 00000000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b75000-73b79000 r-xp 00004000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b79000-73b7a000 rw-p 00000000 00:00 0 [anon:.bss]
+73b7a000-73b82000 r--s 00000000 fc:00 706 /system/framework/boot-android.test.base.vdex
+73b82000-73b83000 r--p 00008000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b83000-73b84000 rw-p 00009000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b84000-73b87000 r--p 00000000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b87000-73b88000 r-xp 00003000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b88000-73b89000 r--s 00000000 fc:00 884 /system/framework/boot-com.google.vr.platform.vdex
+73b89000-73b8a000 r--p 00004000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b8a000-73b8b000 rw-p 00005000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b8b000-73b93000 rw-p 00000000 00:05 10267640 [anon:dalvik-non moving space]
+73b93000-77b8b000 ---p 00008000 00:05 10267640 [anon:dalvik-non moving space]
+77b8b000-97b8b000 rw-p 00000000 00:05 10267645 [anon:dalvik-free list large object space]
+97b8b000-99b8b000 rw-p 00000000 00:05 10270989 [anon:dalvik-data-code-cache]
+99b8b000-9bb8b000 r-xp 00000000 00:05 10270990 [anon:dalvik-jit-code-cache]
+ebad6000-ebad7000 ---p 00000000 00:05 10269717 [anon:dalvik-Sentinel fault page]
+7ffb6e000-7ffb76000 rw-s 000e5000 00:10 20630 /dev/kgsl-3d0
+7ffb76000-7ffb78000 rw-s 000e0000 00:10 20630 /dev/kgsl-3d0
+7ffbc3000-7ffbc4000 rw-s 000e8000 00:10 20630 /dev/kgsl-3d0
+7ffbc4000-7ffbc5000 rw-s 000e7000 00:10 20630 /dev/kgsl-3d0
+7ffbc6000-7ffbce000 rw-s 000e4000 00:10 20630 /dev/kgsl-3d0
+7ffbd0000-7ffbd2000 rw-s 000df000 00:10 20630 /dev/kgsl-3d0
+7ffbd2000-7ffbd4000 rw-s 000de000 00:10 20630 /dev/kgsl-3d0
+7ffbd4000-7ffbd6000 rw-s 000dd000 00:10 20630 /dev/kgsl-3d0
+7ffbd6000-7ffbd8000 rw-s 000dc000 00:10 20630 /dev/kgsl-3d0
+7ffbd8000-7ffbda000 rw-s 000db000 00:10 20630 /dev/kgsl-3d0
+7ffbda000-7ffbdc000 rw-s 000da000 00:10 20630 /dev/kgsl-3d0
+7ffbdd000-7ffbde000 rw-s 000ec000 00:10 20630 /dev/kgsl-3d0
+7ffbde000-7ffbe0000 rw-s 000d8000 00:10 20630 /dev/kgsl-3d0
+7ffce1000-7ffce2000 rw-s 000e6000 00:10 20630 /dev/kgsl-3d0
+7ffce2000-7ffce4000 rw-s 000d9000 00:10 20630 /dev/kgsl-3d0
+7ffce4000-7ffce8000 rw-s 000d4000 00:10 20630 /dev/kgsl-3d0
+7ffce8000-7ffcf8000 rw-s 000d2000 00:10 20630 /dev/kgsl-3d0
+7ffcf8000-7ffd08000 rw-s 000d1000 00:10 20630 /dev/kgsl-3d0
+7ffd08000-7ffd10000 rw-s 000d0000 00:10 20630 /dev/kgsl-3d0
+7ffd14000-7ffd18000 rw-s 000cd000 00:10 20630 /dev/kgsl-3d0
+7ffd18000-7ffd28000 rw-s 000cc000 00:10 20630 /dev/kgsl-3d0
+7ffd28000-7ffd38000 rw-s 000cb000 00:10 20630 /dev/kgsl-3d0
+7ffd38000-7ffd48000 rw-s 000ca000 00:10 20630 /dev/kgsl-3d0
+7ffd48000-7ffd58000 rw-s 000c9000 00:10 20630 /dev/kgsl-3d0
+7ffd58000-7ffd68000 rw-s 000c8000 00:10 20630 /dev/kgsl-3d0
+7ffd68000-7ffd6c000 rw-s 000c7000 00:10 20630 /dev/kgsl-3d0
+7ffdb1000-7ffdb2000 rw-s 000e3000 00:10 20630 /dev/kgsl-3d0
+7ffdb4000-7ffdb5000 rw-s 000e2000 00:10 20630 /dev/kgsl-3d0
+7ffdb5000-7ffdb7000 rw-s 000d7000 00:10 20630 /dev/kgsl-3d0
+7ffdb7000-7ffdb8000 rw-s 000c2000 00:10 20630 /dev/kgsl-3d0
+7ffdb8000-7ffdbc000 rw-s 000c0000 00:10 20630 /dev/kgsl-3d0
+7ffdbc000-7ffdc0000 rw-s 000be000 00:10 20630 /dev/kgsl-3d0
+7ffdc0000-7ffe00000 rw-s 000bb000 00:10 20630 /dev/kgsl-3d0
+7ffe00000-7ffe20000 rw-s 000ba000 00:10 20630 /dev/kgsl-3d0
+7ffe20000-7ffee0000 rw-s 000b9000 00:10 20630 /dev/kgsl-3d0
+7ffee1000-7ffee3000 rw-s 000c1000 00:10 20630 /dev/kgsl-3d0
+7ffee3000-7ffee4000 rw-s 000bf000 00:10 20630 /dev/kgsl-3d0
+7ffee4000-7ffee8000 rw-s 000bd000 00:10 20630 /dev/kgsl-3d0
+7ffee8000-7ffee9000 rw-s 000bc000 00:10 20630 /dev/kgsl-3d0
+7ffeea000-7ffeeb000 rw-s 000e1000 00:10 20630 /dev/kgsl-3d0
+7ffeeb000-7ffeec000 rw-s 000b6000 00:10 20630 /dev/kgsl-3d0
+7ffeec000-7ffeed000 rw-s 000b5000 00:10 20630 /dev/kgsl-3d0
+7ffeed000-7ffeee000 rw-s 000b4000 00:10 20630 /dev/kgsl-3d0
+7ffeee000-7ffeef000 rw-s 000b3000 00:10 20630 /dev/kgsl-3d0
+7ffeef000-7ffef0000 rw-s 000b2000 00:10 20630 /dev/kgsl-3d0
+7ffef0000-7ffef1000 rw-s 000b1000 00:10 20630 /dev/kgsl-3d0
+7ffef1000-7ffef2000 rw-s 000b0000 00:10 20630 /dev/kgsl-3d0
+7ffef2000-7ffef3000 rw-s 000af000 00:10 20630 /dev/kgsl-3d0
+7ffef3000-7ffef4000 rw-s 000ae000 00:10 20630 /dev/kgsl-3d0
+7ffef4000-7ffef5000 rw-s 000ad000 00:10 20630 /dev/kgsl-3d0
+7ffef5000-7ffef6000 rw-s 000ac000 00:10 20630 /dev/kgsl-3d0
+7ffef6000-7ffef7000 rw-s 000ab000 00:10 20630 /dev/kgsl-3d0
+7ffef7000-7ffef8000 rw-s 000aa000 00:10 20630 /dev/kgsl-3d0
+7ffef8000-7ffef9000 rw-s 000a9000 00:10 20630 /dev/kgsl-3d0
+7ffef9000-7ffefa000 rw-s 000a8000 00:10 20630 /dev/kgsl-3d0
+7ffefa000-7ffefb000 rw-s 000a7000 00:10 20630 /dev/kgsl-3d0
+7ffefb000-7ffefc000 rw-s 000a6000 00:10 20630 /dev/kgsl-3d0
+7ffefc000-7ffefd000 rw-s 000a5000 00:10 20630 /dev/kgsl-3d0
+7ffefd000-7ffefe000 rw-s 000a4000 00:10 20630 /dev/kgsl-3d0
+7ffefe000-7ffeff000 rw-s 000a3000 00:10 20630 /dev/kgsl-3d0
+7ffeff000-7fff00000 rw-s 000a2000 00:10 20630 /dev/kgsl-3d0
+7fff00000-7fff01000 rw-s 000a1000 00:10 20630 /dev/kgsl-3d0
+7fff01000-7fff02000 rw-s 000a0000 00:10 20630 /dev/kgsl-3d0
+7fff02000-7fff03000 rw-s 0009f000 00:10 20630 /dev/kgsl-3d0
+7fff03000-7fff04000 rw-s 0009e000 00:10 20630 /dev/kgsl-3d0
+7fff04000-7fff05000 rw-s 0009d000 00:10 20630 /dev/kgsl-3d0
+7fff05000-7fff06000 rw-s 0009c000 00:10 20630 /dev/kgsl-3d0
+7fff06000-7fff07000 rw-s 0009b000 00:10 20630 /dev/kgsl-3d0
+7fff07000-7fff08000 rw-s 0009a000 00:10 20630 /dev/kgsl-3d0
+7fff08000-7fff09000 rw-s 00099000 00:10 20630 /dev/kgsl-3d0
+7fff09000-7fff0a000 rw-s 00098000 00:10 20630 /dev/kgsl-3d0
+7fff0a000-7fff0b000 rw-s 00097000 00:10 20630 /dev/kgsl-3d0
+7fff0b000-7fff0c000 rw-s 00096000 00:10 20630 /dev/kgsl-3d0
+7fff0c000-7fff0d000 rw-s 00095000 00:10 20630 /dev/kgsl-3d0
+7fff0d000-7fff0e000 rw-s 00094000 00:10 20630 /dev/kgsl-3d0
+7fff0e000-7fff0f000 rw-s 00093000 00:10 20630 /dev/kgsl-3d0
+7fff0f000-7fff10000 rw-s 00092000 00:10 20630 /dev/kgsl-3d0
+7fff10000-7fff11000 rw-s 00091000 00:10 20630 /dev/kgsl-3d0
+7fff11000-7fff12000 rw-s 00090000 00:10 20630 /dev/kgsl-3d0
+7fff12000-7fff13000 rw-s 0008f000 00:10 20630 /dev/kgsl-3d0
+7fff13000-7fff14000 rw-s 0008e000 00:10 20630 /dev/kgsl-3d0
+7fff14000-7fff15000 rw-s 0008d000 00:10 20630 /dev/kgsl-3d0
+7fff15000-7fff16000 rw-s 0008c000 00:10 20630 /dev/kgsl-3d0
+7fff16000-7fff17000 rw-s 0008b000 00:10 20630 /dev/kgsl-3d0
+7fff17000-7fff18000 rw-s 0008a000 00:10 20630 /dev/kgsl-3d0
+7fff18000-7fff19000 rw-s 00089000 00:10 20630 /dev/kgsl-3d0
+7fff19000-7fff1a000 rw-s 00088000 00:10 20630 /dev/kgsl-3d0
+7fff1a000-7fff1b000 rw-s 00087000 00:10 20630 /dev/kgsl-3d0
+7fff1b000-7fff1c000 rw-s 00086000 00:10 20630 /dev/kgsl-3d0
+7fff1c000-7fff1d000 rw-s 00085000 00:10 20630 /dev/kgsl-3d0
+7fff1d000-7fff1e000 rw-s 00084000 00:10 20630 /dev/kgsl-3d0
+7fff1e000-7fff1f000 rw-s 00083000 00:10 20630 /dev/kgsl-3d0
+7fff1f000-7fff20000 rw-s 00082000 00:10 20630 /dev/kgsl-3d0
+7fff20000-7fff21000 rw-s 00081000 00:10 20630 /dev/kgsl-3d0
+7fff21000-7fff22000 rw-s 00080000 00:10 20630 /dev/kgsl-3d0
+7fff22000-7fff23000 rw-s 0007f000 00:10 20630 /dev/kgsl-3d0
+7fff23000-7fff24000 rw-s 0007e000 00:10 20630 /dev/kgsl-3d0
+7fff24000-7fff25000 rw-s 0007d000 00:10 20630 /dev/kgsl-3d0
+7fff25000-7fff26000 rw-s 0007c000 00:10 20630 /dev/kgsl-3d0
+7fff26000-7fff27000 rw-s 0007b000 00:10 20630 /dev/kgsl-3d0
+7fff27000-7fff28000 rw-s 0007a000 00:10 20630 /dev/kgsl-3d0
+7fff28000-7fff29000 rw-s 00079000 00:10 20630 /dev/kgsl-3d0
+7fff29000-7fff2a000 rw-s 00078000 00:10 20630 /dev/kgsl-3d0
+7fff2a000-7fff2b000 rw-s 00077000 00:10 20630 /dev/kgsl-3d0
+7fff2b000-7fff2c000 rw-s 00076000 00:10 20630 /dev/kgsl-3d0
+7fff2c000-7fff2d000 rw-s 00075000 00:10 20630 /dev/kgsl-3d0
+7fff2d000-7fff2e000 rw-s 00074000 00:10 20630 /dev/kgsl-3d0
+7fff2e000-7fff2f000 rw-s 00073000 00:10 20630 /dev/kgsl-3d0
+7fff2f000-7fff30000 rw-s 00072000 00:10 20630 /dev/kgsl-3d0
+7fff30000-7fff31000 rw-s 00071000 00:10 20630 /dev/kgsl-3d0
+7fff31000-7fff32000 rw-s 00070000 00:10 20630 /dev/kgsl-3d0
+7fff32000-7fff33000 rw-s 0006f000 00:10 20630 /dev/kgsl-3d0
+7fff33000-7fff34000 rw-s 0006e000 00:10 20630 /dev/kgsl-3d0
+7fff34000-7fff35000 rw-s 0006d000 00:10 20630 /dev/kgsl-3d0
+7fff35000-7fff36000 rw-s 0006c000 00:10 20630 /dev/kgsl-3d0
+7fff36000-7fff37000 rw-s 0006b000 00:10 20630 /dev/kgsl-3d0
+7fff37000-7fff38000 rw-s 0006a000 00:10 20630 /dev/kgsl-3d0
+7fff38000-7fff39000 rw-s 00069000 00:10 20630 /dev/kgsl-3d0
+7fff39000-7fff3a000 rw-s 00068000 00:10 20630 /dev/kgsl-3d0
+7fff3a000-7fff3b000 rw-s 00067000 00:10 20630 /dev/kgsl-3d0
+7fff3b000-7fff3c000 rw-s 00066000 00:10 20630 /dev/kgsl-3d0
+7fff3c000-7fff3d000 rw-s 00065000 00:10 20630 /dev/kgsl-3d0
+7fff3d000-7fff3e000 rw-s 00064000 00:10 20630 /dev/kgsl-3d0
+7fff3e000-7fff3f000 rw-s 00063000 00:10 20630 /dev/kgsl-3d0
+7fff3f000-7fff40000 rw-s 00062000 00:10 20630 /dev/kgsl-3d0
+7fff40000-7fff41000 rw-s 00061000 00:10 20630 /dev/kgsl-3d0
+7fff41000-7fff42000 rw-s 00060000 00:10 20630 /dev/kgsl-3d0
+7fff42000-7fff43000 rw-s 0005f000 00:10 20630 /dev/kgsl-3d0
+7fff43000-7fff44000 rw-s 0005e000 00:10 20630 /dev/kgsl-3d0
+7fff44000-7fff45000 rw-s 0005d000 00:10 20630 /dev/kgsl-3d0
+7fff45000-7fff46000 rw-s 0005c000 00:10 20630 /dev/kgsl-3d0
+7fff46000-7fff47000 rw-s 0005b000 00:10 20630 /dev/kgsl-3d0
+7fff47000-7fff48000 rw-s 0005a000 00:10 20630 /dev/kgsl-3d0
+7fff48000-7fff49000 rw-s 00059000 00:10 20630 /dev/kgsl-3d0
+7fff49000-7fff4a000 rw-s 00058000 00:10 20630 /dev/kgsl-3d0
+7fff4a000-7fff4b000 rw-s 00057000 00:10 20630 /dev/kgsl-3d0
+7fff4b000-7fff4c000 rw-s 00056000 00:10 20630 /dev/kgsl-3d0
+7fff4c000-7fff4d000 rw-s 00055000 00:10 20630 /dev/kgsl-3d0
+7fff4d000-7fff4e000 rw-s 00054000 00:10 20630 /dev/kgsl-3d0
+7fff4e000-7fff4f000 rw-s 00053000 00:10 20630 /dev/kgsl-3d0
+7fff4f000-7fff50000 rw-s 00052000 00:10 20630 /dev/kgsl-3d0
+7fff50000-7fff51000 rw-s 00051000 00:10 20630 /dev/kgsl-3d0
+7fff51000-7fff52000 rw-s 00050000 00:10 20630 /dev/kgsl-3d0
+7fff52000-7fff53000 rw-s 0004f000 00:10 20630 /dev/kgsl-3d0
+7fff53000-7fff54000 rw-s 0004e000 00:10 20630 /dev/kgsl-3d0
+7fff54000-7fff55000 rw-s 0004d000 00:10 20630 /dev/kgsl-3d0
+7fff55000-7fff56000 rw-s 0004c000 00:10 20630 /dev/kgsl-3d0
+7fff56000-7fff57000 rw-s 0004b000 00:10 20630 /dev/kgsl-3d0
+7fff57000-7fff58000 rw-s 0004a000 00:10 20630 /dev/kgsl-3d0
+7fff58000-7fff59000 rw-s 00049000 00:10 20630 /dev/kgsl-3d0
+7fff59000-7fff5a000 rw-s 00048000 00:10 20630 /dev/kgsl-3d0
+7fff5a000-7fff5b000 rw-s 00047000 00:10 20630 /dev/kgsl-3d0
+7fff5b000-7fff5c000 rw-s 00046000 00:10 20630 /dev/kgsl-3d0
+7fff5c000-7fff5d000 rw-s 00045000 00:10 20630 /dev/kgsl-3d0
+7fff5d000-7fff5e000 rw-s 00044000 00:10 20630 /dev/kgsl-3d0
+7fff5e000-7fff5f000 rw-s 00043000 00:10 20630 /dev/kgsl-3d0
+7fff5f000-7fff60000 rw-s 00042000 00:10 20630 /dev/kgsl-3d0
+7fff60000-7fff61000 rw-s 00041000 00:10 20630 /dev/kgsl-3d0
+7fff61000-7fff62000 rw-s 00040000 00:10 20630 /dev/kgsl-3d0
+7fff62000-7fff63000 rw-s 0003f000 00:10 20630 /dev/kgsl-3d0
+7fff63000-7fff64000 rw-s 0003e000 00:10 20630 /dev/kgsl-3d0
+7fff64000-7fff65000 rw-s 0003d000 00:10 20630 /dev/kgsl-3d0
+7fff65000-7fff66000 rw-s 0003c000 00:10 20630 /dev/kgsl-3d0
+7fff66000-7fff67000 rw-s 0003b000 00:10 20630 /dev/kgsl-3d0
+7fff67000-7fff68000 rw-s 0003a000 00:10 20630 /dev/kgsl-3d0
+7fff68000-7fff69000 rw-s 00039000 00:10 20630 /dev/kgsl-3d0
+7fff69000-7fff6a000 rw-s 00038000 00:10 20630 /dev/kgsl-3d0
+7fff6a000-7fff6b000 rw-s 00037000 00:10 20630 /dev/kgsl-3d0
+7fff6b000-7fff6c000 rw-s 00036000 00:10 20630 /dev/kgsl-3d0
+7fff6c000-7fff6d000 rw-s 00035000 00:10 20630 /dev/kgsl-3d0
+7fff6d000-7fff6e000 rw-s 00034000 00:10 20630 /dev/kgsl-3d0
+7fff6e000-7fff6f000 rw-s 00033000 00:10 20630 /dev/kgsl-3d0
+7fff6f000-7fff70000 rw-s 00032000 00:10 20630 /dev/kgsl-3d0
+7fff70000-7fff71000 rw-s 00031000 00:10 20630 /dev/kgsl-3d0
+7fff71000-7fff72000 rw-s 00030000 00:10 20630 /dev/kgsl-3d0
+7fff72000-7fff73000 rw-s 0002f000 00:10 20630 /dev/kgsl-3d0
+7fff73000-7fff74000 rw-s 0002e000 00:10 20630 /dev/kgsl-3d0
+7fff74000-7fff75000 rw-s 0002d000 00:10 20630 /dev/kgsl-3d0
+7fff75000-7fff76000 rw-s 0002c000 00:10 20630 /dev/kgsl-3d0
+7fff76000-7fff77000 rw-s 0002b000 00:10 20630 /dev/kgsl-3d0
+7fff77000-7fff78000 rw-s 0002a000 00:10 20630 /dev/kgsl-3d0
+7fff78000-7fff79000 rw-s 00029000 00:10 20630 /dev/kgsl-3d0
+7fff79000-7fff7a000 rw-s 00028000 00:10 20630 /dev/kgsl-3d0
+7fff7a000-7fff7b000 rw-s 00027000 00:10 20630 /dev/kgsl-3d0
+7fff7b000-7fff7c000 rw-s 00026000 00:10 20630 /dev/kgsl-3d0
+7fff7c000-7fff7d000 rw-s 00025000 00:10 20630 /dev/kgsl-3d0
+7fff7d000-7fff7e000 rw-s 00024000 00:10 20630 /dev/kgsl-3d0
+7fff7e000-7fff7f000 rw-s 00023000 00:10 20630 /dev/kgsl-3d0
+7fff7f000-7fff80000 rw-s 00022000 00:10 20630 /dev/kgsl-3d0
+7fff80000-7fff90000 rw-s 00019000 00:10 20630 /dev/kgsl-3d0
+7fff90000-7fffb0000 rw-s 00018000 00:10 20630 /dev/kgsl-3d0
+7fffb1000-7fffb2000 rw-s 00021000 00:10 20630 /dev/kgsl-3d0
+7fffb2000-7fffb3000 rw-s 00020000 00:10 20630 /dev/kgsl-3d0
+7fffb3000-7fffb4000 rw-s 0001f000 00:10 20630 /dev/kgsl-3d0
+7fffba000-7fffbe000 rw-s 0001b000 00:10 20630 /dev/kgsl-3d0
+7fffbe000-7fffbf000 rw-s 0001a000 00:10 20630 /dev/kgsl-3d0
+7fffbf000-7fffc0000 rw-s 00017000 00:10 20630 /dev/kgsl-3d0
+7fffc0000-7fffe0000 rw-s 00016000 00:10 20630 /dev/kgsl-3d0
+7fffe0000-7fffe1000 rw-s 00014000 00:10 20630 /dev/kgsl-3d0
+7fffe1000-7fffe5000 rw-s 00013000 00:10 20630 /dev/kgsl-3d0
+7fffe5000-7fffe6000 rw-s 00012000 00:10 20630 /dev/kgsl-3d0
+7fffe6000-7fffe7000 rw-s 00011000 00:10 20630 /dev/kgsl-3d0
+7fffe7000-7fffe8000 rw-s 00010000 00:10 20630 /dev/kgsl-3d0
+7fffe8000-7fffe9000 rw-s 0000f000 00:10 20630 /dev/kgsl-3d0
+7fffe9000-7fffea000 rw-s 0000e000 00:10 20630 /dev/kgsl-3d0
+7fffea000-7fffeb000 rw-s 0000d000 00:10 20630 /dev/kgsl-3d0
+7fffeb000-7fffec000 rw-s 0000c000 00:10 20630 /dev/kgsl-3d0
+7fffec000-7ffff0000 rw-s 0000b000 00:10 20630 /dev/kgsl-3d0
+7ffff0000-7ffff1000 rw-s 0000a000 00:10 20630 /dev/kgsl-3d0
+7ffff1000-7ffff5000 rw-s 00009000 00:10 20630 /dev/kgsl-3d0
+7ffff5000-7ffff6000 rw-s 00008000 00:10 20630 /dev/kgsl-3d0
+7ffff6000-7ffff7000 rw-s 00007000 00:10 20630 /dev/kgsl-3d0
+7ffff7000-7ffff8000 rw-s 00006000 00:10 20630 /dev/kgsl-3d0
+7ffff8000-7ffff9000 rw-s 00005000 00:10 20630 /dev/kgsl-3d0
+7ffff9000-7ffffa000 rw-s 00004000 00:10 20630 /dev/kgsl-3d0
+7ffffa000-7ffffb000 rw-s 00003000 00:10 20630 /dev/kgsl-3d0
+7ffffb000-7ffffc000 rw-s 00002000 00:10 20630 /dev/kgsl-3d0
+7ffffc000-800000000 rw-s 00001000 00:10 20630 /dev/kgsl-3d0
+5ff1d4f000-5ff1d54000 r-xp 00000000 fc:00 3419 /system/bin/app_process64
+5ff1d6e000-5ff1d6f000 r--p 0000f000 fc:00 3419 /system/bin/app_process64
+5ff1d6f000-5ff1d71000 rw-p 00000000 00:00 0
+704defa000-704defb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704defb000-704defc000 ---p 00000000 00:00 0
+704defc000-704e000000 rw-p 00000000 00:00 0
+704e000000-704e400000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+704e455000-704e456000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704e456000-704e457000 ---p 00000000 00:00 0
+704e457000-704e553000 rw-p 00000000 00:00 0
+704e553000-704e651000 r--p 00000000 00:10 16029 /dev/hwbinder
+704e651000-704e65f000 r-xp 00000000 fc:01 1040 /vendor/lib64/egl/eglSubDriverAndroid.so
+704e65f000-704e660000 r--p 0000e000 fc:01 1040 /vendor/lib64/egl/eglSubDriverAndroid.so
+704e660000-704e661000 rw-p 0000f000 fc:01 1040 /vendor/lib64/egl/eglSubDriverAndroid.so
+704e69d000-704e69e000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704e69e000-704e79b000 rw-p 00000000 00:00 0
+704e79b000-704f79b000 rw-s 00000000 00:05 10271021 /dev/ashmem/AudioFlinger::Client(29312) (deleted)
+704f79b000-704f79c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704f79c000-704f899000 rw-p 00000000 00:00 0
+704f899000-704f89a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704f89a000-704f89b000 ---p 00000000 00:00 0
+704f89b000-704f997000 rw-p 00000000 00:00 0
+704f997000-704f9ee000 r-xp 00000000 103:1d 1737338 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704f9ee000-704f9fd000 ---p 00000000 00:00 0
+704f9fd000-704fa00000 r--p 00056000 103:1d 1737338 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704fa00000-704fa01000 rw-p 00059000 103:1d 1737338 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704fa01000-704fa19000 rw-p 00000000 00:00 0 [anon:.bss]
+704fa40000-70507e7000 r-xp 00000000 fc:01 1026 /vendor/lib64/libllvm-glnext.so
+70507e7000-70507fc000 ---p 00000000 00:00 0
+70507fc000-7050835000 r--p 00da7000 fc:01 1026 /vendor/lib64/libllvm-glnext.so
+7050835000-705083a000 rw-p 00de0000 fc:01 1026 /vendor/lib64/libllvm-glnext.so
+705083a000-7050855000 rw-p 00000000 00:00 0 [anon:.bss]
+705089b000-7050f19000 r-xp 00000000 fc:01 1039 /vendor/lib64/egl/libGLESv2_adreno.so
+7050f19000-7050f22000 r--p 0067e000 fc:01 1039 /vendor/lib64/egl/libGLESv2_adreno.so
+7050f22000-7050f29000 rw-p 00687000 fc:01 1039 /vendor/lib64/egl/libGLESv2_adreno.so
+7050f29000-7050f2c000 rw-p 00000000 00:00 0 [anon:.bss]
+7050f83000-7050fbc000 r-xp 00000000 fc:01 1041 /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbc000-7050fbd000 r--p 00039000 fc:01 1041 /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbd000-7050fbe000 rw-p 0003a000 fc:01 1041 /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbe000-7050fbf000 rw-p 00000000 00:00 0 [anon:.bss]
+7050fc6000-705111d000 r-xp 00000000 fc:01 865 /vendor/lib64/libgsl.so
+705111d000-705111e000 r--p 00157000 fc:01 865 /vendor/lib64/libgsl.so
+705111e000-705111f000 rw-p 00158000 fc:01 865 /vendor/lib64/libgsl.so
+705111f000-7051120000 rw-p 00000000 00:00 0 [anon:.bss]
+7051146000-705115d000 r-xp 00000000 fc:00 2587 /system/lib64/vndk-sp-28/libz.so
+705115d000-7051175000 ---p 00000000 00:00 0
+7051175000-7051176000 r--p 0001f000 fc:00 2587 /system/lib64/vndk-sp-28/libz.so
+7051176000-7051177000 rw-p 00020000 fc:00 2587 /system/lib64/vndk-sp-28/libz.so
+705119f000-70511ac000 r-xp 00000000 fc:01 886 /vendor/lib64/libadreno_utils.so
+70511ac000-70511ad000 r--p 0000d000 fc:01 886 /vendor/lib64/libadreno_utils.so
+70511ad000-70511ae000 rw-p 0000e000 fc:01 886 /vendor/lib64/libadreno_utils.so
+70511ae000-70511b0000 rw-p 00000000 00:00 0 [anon:.bss]
+70511c0000-70511d7000 r-xp 00000000 fc:01 1044 /vendor/lib64/egl/libEGL_adreno.so
+70511d7000-70511d8000 r--p 00017000 fc:01 1044 /vendor/lib64/egl/libEGL_adreno.so
+70511d8000-70511d9000 rw-p 00018000 fc:01 1044 /vendor/lib64/egl/libEGL_adreno.so
+70511d9000-70511da000 rw-p 00000000 00:00 0 [anon:.bss]
+705120a000-705120d000 r-xp 00000000 fc:01 972 /vendor/lib64/libdrmutils.so
+705120d000-7051229000 ---p 00000000 00:00 0
+7051229000-705122a000 r--p 0000f000 fc:01 972 /vendor/lib64/libdrmutils.so
+705122a000-705122b000 rw-p 00010000 fc:01 972 /vendor/lib64/libdrmutils.so
+705125a000-705125c000 r-xp 00000000 fc:01 1046 /vendor/lib64/libqdMetaData.so
+705125c000-7051279000 ---p 00000000 00:00 0
+7051279000-705127a000 r--p 0000f000 fc:01 1046 /vendor/lib64/libqdMetaData.so
+705127a000-705127b000 rw-p 00010000 fc:01 1046 /vendor/lib64/libqdMetaData.so
+7051286000-7051297000 r-xp 00000000 fc:01 1024 /vendor/lib64/libdrm.so
+7051297000-70512b5000 ---p 00000000 00:00 0
+70512b5000-70512b6000 r--p 0001f000 fc:01 1024 /vendor/lib64/libdrm.so
+70512b6000-70512b7000 rw-p 00020000 fc:01 1024 /vendor/lib64/libdrm.so
+70512cb000-70512de000 r-xp 00000000 fc:01 1008 /vendor/lib64/hw/gralloc.msm8998.so
+70512de000-70512fa000 ---p 00000000 00:00 0
+70512fa000-70512fb000 r--p 0001f000 fc:01 1008 /vendor/lib64/hw/gralloc.msm8998.so
+70512fb000-70512fc000 rw-p 00020000 fc:01 1008 /vendor/lib64/hw/gralloc.msm8998.so
+7051326000-7051327000 ---p 00000000 00:00 0 [anon:thread stack guard]
+7051327000-7051328000 ---p 00000000 00:00 0
+7051328000-7051424000 rw-p 00000000 00:00 0
+7051424000-705143d000 r--p 00000000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705143d000-7051480000 r-xp 00019000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+7051480000-7051494000 r--p 00211000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+7051494000-705149f000 r--p 000c5000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+705149f000-70514a2000 r--p 00032000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+70514a2000-70514a5000 r--p 0002b000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+70514a5000-70514ac000 r--p 0003f000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+70514ac000-70514b2000 r--p 00044000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+70514b2000-70514bd000 r--p 00035000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+70514bd000-70514f4000 r--p 0060f000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70514f4000-70514fe000 r--p 00054000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70514fe000-70514ff000 r--p 0000c000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70514ff000-7051500000 r--p 0000e000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+7051500000-7051501000 r--p 00004000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7051501000-7051502000 r--p 00004000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+7051502000-7051503000 r--p 00001000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+7051503000-7051504000 rw-p 00000000 00:00 0 [anon:.bss]
+7051504000-7051579000 r--s 00000000 fc:00 790 /system/framework/oat/arm64/org.apache.http.legacy.boot.vdex
+7051579000-705157a000 r--p 0005c000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705157a000-705157b000 rw-p 0005d000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705158b000-7057f4d000 ---p 00000000 00:00 0
+7057f4d000-7057f4f000 r-xp 00000000 fc:00 2646 /system/lib64/libwebviewchromium_loader.so
+7057f4f000-7057f6c000 ---p 00000000 00:00 0
+7057f6c000-7057f6d000 r--p 0000f000 fc:00 2646 /system/lib64/libwebviewchromium_loader.so
+7057f6d000-7057f6e000 rw-p 00010000 fc:00 2646 /system/lib64/libwebviewchromium_loader.so
+7057f76000-7057f96000 r--s 00000000 00:10 16615 /dev/__properties__/u:object_r:hwservicemanager_prop:s0
+7057f96000-7057fb6000 r--s 00000000 00:10 16639 /dev/__properties__/u:object_r:public_vendor_default_prop:s0
+7057fb6000-7058004000 r--s 00000000 fc:00 1112 /system/usr/hyphen-data/hyph-hu.hyb
+7058004000-7058024000 r-xp 00000000 fc:00 2354 /system/lib64/libcompiler_rt.so
+7058024000-7058043000 ---p 00000000 00:00 0
+7058043000-7058044000 r--p 0002f000 fc:00 2354 /system/lib64/libcompiler_rt.so
+7058044000-7058045000 rw-p 00030000 fc:00 2354 /system/lib64/libcompiler_rt.so
+7058045000-70580b2000 rw-p 00000000 00:00 0 [anon:.bss]
+70580bd000-70580dd000 rw-p 00000000 00:05 10265386 [anon:dalvik-LinearAlloc]
+70580dd000-70580df000 r-xp 00000000 fc:00 2597 /system/lib64/vndk-sp-28/libhardware.so
+70580df000-70580fc000 ---p 00000000 00:00 0
+70580fc000-70580fd000 r--p 0000f000 fc:00 2597 /system/lib64/vndk-sp-28/libhardware.so
+70580fd000-70580fe000 rw-p 00010000 fc:00 2597 /system/lib64/vndk-sp-28/libhardware.so
+705810e000-705811f000 r-xp 00000000 fc:00 2589 /system/lib64/vndk-sp-28/libbase.so
+705811f000-705813d000 ---p 00000000 00:00 0
+705813d000-705813e000 r--p 0001f000 fc:00 2589 /system/lib64/vndk-sp-28/libbase.so
+705813e000-705813f000 rw-p 00020000 fc:00 2589 /system/lib64/vndk-sp-28/libbase.so
+7058140000-7058167000 r-xp 00000000 fc:00 2572 /system/lib64/vndk-sp-28/libhwbinder.so
+7058167000-705817d000 ---p 00000000 00:00 0
+705817d000-705817f000 r--p 0002e000 fc:00 2572 /system/lib64/vndk-sp-28/libhwbinder.so
+705817f000-7058180000 rw-p 00030000 fc:00 2572 /system/lib64/vndk-sp-28/libhwbinder.so
+705818c000-705818d000 r-xp 00000000 fc:00 2584 /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+705818d000-70581ab000 ---p 00000000 00:00 0
+70581ab000-70581ac000 r--p 0000f000 fc:00 2584 /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+70581ac000-70581ad000 rw-p 00010000 fc:00 2584 /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+70581b7000-70581d7000 r--s 00000000 00:10 16619 /dev/__properties__/u:object_r:log_prop:s0
+70581d7000-7058237000 r-xp 00000000 fc:00 2574 /system/lib64/vndk-sp-28/libhidltransport.so
+7058237000-7058255000 ---p 00000000 00:00 0
+7058255000-705825d000 r--p 00068000 fc:00 2574 /system/lib64/vndk-sp-28/libhidltransport.so
+705825d000-705825e000 rw-p 00070000 fc:00 2574 /system/lib64/vndk-sp-28/libhidltransport.so
+7058260000-7058284000 r--s 00000000 fc:00 1138 /system/usr/hyphen-data/hyph-nn.hyb
+7058284000-70582a0000 r-xp 00000000 fc:00 2576 /system/lib64/vndk-sp-28/libutils.so
+70582a0000-70582b3000 ---p 00000000 00:00 0
+70582b3000-70582b4000 r--p 0001f000 fc:00 2576 /system/lib64/vndk-sp-28/libutils.so
+70582b4000-70582b5000 rw-p 00020000 fc:00 2576 /system/lib64/vndk-sp-28/libutils.so
+70582c4000-7058391000 r-xp 00000000 fc:00 2568 /system/lib64/vndk-sp-28/libc++.so
+7058391000-70583ad000 ---p 00000000 00:00 0
+70583ad000-70583b7000 r--p 000d6000 fc:00 2568 /system/lib64/vndk-sp-28/libc++.so
+70583b7000-70583b8000 rw-p 000e0000 fc:00 2568 /system/lib64/vndk-sp-28/libc++.so
+70583b8000-70583bb000 rw-p 00000000 00:00 0 [anon:.bss]
+70583cd000-70583e4000 r-xp 00000000 fc:00 2580 /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+70583e4000-70583f9000 ---p 00000000 00:00 0
+70583f9000-70583fb000 r--p 0001e000 fc:00 2580 /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+70583fb000-70583fc000 rw-p 00020000 fc:00 2580 /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+705841b000-7058421000 r-xp 00000000 fc:01 1001 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+7058421000-705843a000 ---p 00000000 00:00 0
+705843a000-705843b000 r--p 0000f000 fc:01 1001 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+705843b000-705843c000 rw-p 00010000 fc:01 1001 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+705844f000-7058473000 r--s 00000000 fc:00 1150 /system/usr/hyphen-data/hyph-nb.hyb
+7058473000-7058495000 r-xp 00000000 fc:00 2582 /system/lib64/vndk-sp-28/libhidlbase.so
+7058495000-70584b1000 ---p 00000000 00:00 0
+70584b1000-70584b3000 r--p 0002e000 fc:00 2582 /system/lib64/vndk-sp-28/libhidlbase.so
+70584b3000-70584b4000 rw-p 00030000 fc:00 2582 /system/lib64/vndk-sp-28/libhidlbase.so
+70584cd000-70584df000 r-xp 00000000 fc:00 2595 /system/lib64/vndk-sp-28/libcutils.so
+70584df000-70584fb000 ---p 00000000 00:00 0
+70584fb000-70584fd000 r--p 0001e000 fc:00 2595 /system/lib64/vndk-sp-28/libcutils.so
+70584fd000-70584fe000 rw-p 00020000 fc:00 2595 /system/lib64/vndk-sp-28/libcutils.so
+7058519000-7058537000 r--s 00000000 fc:00 1124 /system/usr/hyphen-data/hyph-de-ch-1901.hyb
+7058537000-7059fd1000 r--s 0070b000 fc:00 989 /system/framework/framework-res.apk
+7059fd1000-705a013000 r-xp 00000000 fc:00 2610 /system/lib64/libjavacrypto.so
+705a013000-705a02b000 ---p 00000000 00:00 0
+705a02b000-705a02d000 r--p 0004e000 fc:00 2610 /system/lib64/libjavacrypto.so
+705a02d000-705a02f000 rw-p 00050000 fc:00 2610 /system/lib64/libjavacrypto.so
+705a041000-705a05f000 r--s 00000000 fc:00 1128 /system/usr/hyphen-data/hyph-de-1996.hyb
+705a05f000-705a06a000 r-xp 00000000 fc:00 2917 /system/lib64/libsoundpool.so
+705a06a000-705a07e000 ---p 00000000 00:00 0
+705a07e000-705a07f000 r--p 0000f000 fc:00 2917 /system/lib64/libsoundpool.so
+705a07f000-705a080000 rw-p 00010000 fc:00 2917 /system/lib64/libsoundpool.so
+705a087000-705a102000 r--s 00000000 fc:00 1246 /system/usr/share/zoneinfo/tzdata
+705a102000-705a863000 r--s 00000000 fc:00 101 /system/fonts/NotoColorEmoji.ttf
+705a863000-705c000000 r--s 00000000 fc:00 251 /system/fonts/NotoSerifCJK-Regular.ttc
+705c000000-705c200000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+705c209000-705c227000 r--s 00000000 fc:00 1077 /system/usr/hyphen-data/hyph-de-1901.hyb
+705c227000-705c26e000 r--s 02284000 fc:00 989 /system/framework/framework-res.apk
+705c26e000-705d43e000 r--s 00000000 fc:00 95 /system/fonts/NotoSansCJK-Regular.ttc
+705d43e000-705d4ec000 r--s 00000000 fc:00 278 /system/fonts/NotoSansSymbols-Regular-Subsetted.ttf
+705d4ec000-705d548000 r--s 00000000 fc:00 233 /system/fonts/NotoSansTibetan-Bold.ttf
+705d548000-705d5ab000 r--s 00000000 fc:00 177 /system/fonts/NotoSansTibetan-Regular.ttf
+705d5ab000-705d627000 r--s 00000000 fc:00 197 /system/fonts/NotoSansEgyptianHieroglyphs-Regular.ttf
+705d627000-705d6a2000 r--s 00000000 fc:00 76 /system/fonts/NotoSansCuneiform-Regular.ttf
+705d6a2000-705d6f3000 r--s 00000000 fc:00 67 /system/fonts/RobotoCondensed-BoldItalic.ttf
+705d6f3000-705d73e000 r--s 00000000 fc:00 199 /system/fonts/RobotoCondensed-Bold.ttf
+705d73e000-705d78f000 r--s 00000000 fc:00 230 /system/fonts/RobotoCondensed-MediumItalic.ttf
+705d78f000-705d7da000 r--s 00000000 fc:00 92 /system/fonts/RobotoCondensed-Medium.ttf
+705d7da000-705d82b000 r--s 00000000 fc:00 128 /system/fonts/RobotoCondensed-Italic.ttf
+705d82b000-705d875000 r--s 00000000 fc:00 164 /system/fonts/RobotoCondensed-Regular.ttf
+705d875000-705d8c7000 r--s 00000000 fc:00 292 /system/fonts/RobotoCondensed-LightItalic.ttf
+705d8c7000-705d919000 r--s 00000000 fc:00 85 /system/fonts/Roboto-BoldItalic.ttf
+705d919000-705d964000 r--s 00000000 fc:00 175 /system/fonts/Roboto-Bold.ttf
+705d964000-705d9b5000 r--s 00000000 fc:00 266 /system/fonts/Roboto-BlackItalic.ttf
+705d9b5000-705da00000 r--s 00000000 fc:00 187 /system/fonts/Roboto-Black.ttf
+705da00000-705dc00000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+705dc1d000-705dc6e000 r--s 00000000 fc:00 148 /system/fonts/Roboto-MediumItalic.ttf
+705dc6e000-705dcb9000 r--s 00000000 fc:00 284 /system/fonts/Roboto-Medium.ttf
+705dcb9000-705dd0a000 r--s 00000000 fc:00 105 /system/fonts/Roboto-Italic.ttf
+705dd0a000-705dd55000 r--s 00000000 fc:00 156 /system/fonts/Roboto-Regular.ttf
+705dd55000-705dda7000 r--s 00000000 fc:00 217 /system/fonts/Roboto-LightItalic.ttf
+705dda7000-705ddf8000 r--s 00000000 fc:00 166 /system/fonts/Roboto-ThinItalic.ttf
+705ddf8000-705ddf9000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705ddf9000-705ddfa000 ---p 00000000 00:00 0
+705ddfa000-705def6000 rw-p 00000000 00:00 0
+705def6000-705f5ec000 r--s 00000000 fc:00 1350 /system/usr/icu/icudt60l.dat
+705f5ec000-705f5ed000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f5ed000-705f5ee000 ---p 00000000 00:00 0
+705f5ee000-705f6ea000 rw-p 00000000 00:00 0
+705f6ea000-705f7e8000 r--p 00000000 00:10 20636 /dev/binder
+705f7e8000-705f7e9000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f7e9000-705f7ea000 ---p 00000000 00:00 0
+705f7ea000-705f8ee000 rw-p 00000000 00:00 0
+705f8ee000-705f8ef000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f8ef000-705f8f0000 ---p 00000000 00:00 0
+705f8f0000-705f9f4000 rw-p 00000000 00:00 0
+705f9f4000-705f9f5000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f9f5000-705f9f6000 ---p 00000000 00:00 0
+705f9f6000-705fafa000 rw-p 00000000 00:00 0
+705fafa000-705fafb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705fafb000-705fafc000 ---p 00000000 00:00 0
+705fafc000-705fc00000 rw-p 00000000 00:00 0
+705fc00000-705fe00000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+705fe01000-705fe4c000 r--s 00000000 fc:00 97 /system/fonts/Roboto-Light.ttf
+705fe4c000-705fe4d000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705fe4d000-705fe4e000 ---p 00000000 00:00 0
+705fe4e000-705ff4a000 rw-p 00000000 00:00 0
+705ff4a000-705ff4b000 ---p 00000000 00:05 10270991 [anon:dalvik-Jit thread pool worker thread 0]
+705ff4b000-705ff4c000 ---p 00001000 00:05 10270991 [anon:dalvik-Jit thread pool worker thread 0]
+705ff4c000-706004b000 rw-p 00002000 00:05 10270991 [anon:dalvik-Jit thread pool worker thread 0]
+706004b000-706010f000 r-xp 00000000 fc:00 2390 /system/lib64/libvixl-arm64.so
+706010f000-7060120000 ---p 00000000 00:00 0
+7060120000-7060125000 r--p 000cb000 fc:00 2390 /system/lib64/libvixl-arm64.so
+7060125000-7060126000 rw-p 000d0000 fc:00 2390 /system/lib64/libvixl-arm64.so
+7060126000-706012d000 rw-p 00000000 00:00 0 [anon:.bss]
+7060135000-7060151000 r--s 00000000 fc:01 1180 /vendor/overlay/framework-res__auto_generated_rro.apk
+7060151000-7060263000 r-xp 00000000 fc:00 2669 /system/lib64/libvixl-arm.so
+7060263000-7060275000 ---p 00000000 00:00 0
+7060275000-706027a000 r--p 0011b000 fc:00 2669 /system/lib64/libvixl-arm.so
+706027a000-706027b000 rw-p 00120000 fc:00 2669 /system/lib64/libvixl-arm.so
+706028b000-706056c000 r-xp 00000000 fc:00 2972 /system/lib64/libart-compiler.so
+706056c000-7060580000 ---p 00000000 00:00 0
+7060580000-7060598000 r--p 002e8000 fc:00 2972 /system/lib64/libart-compiler.so
+7060598000-7060599000 rw-p 00300000 fc:00 2972 /system/lib64/libart-compiler.so
+7060599000-70605a0000 rw-p 00000000 00:00 0 [anon:.bss]
+70605b0000-70605d0000 r--s 00000000 00:10 16571 /dev/__properties__/u:object_r:config_prop:s0
+70605d0000-7060619000 r-xp 00000000 fc:00 2702 /system/lib64/libssl.so
+7060619000-706062d000 ---p 00000000 00:00 0
+706062d000-7060630000 r--p 0004d000 fc:00 2702 /system/lib64/libssl.so
+7060630000-7060631000 rw-p 00050000 fc:00 2702 /system/lib64/libssl.so
+7060647000-7060667000 r--s 00000000 00:10 16595 /dev/__properties__/u:object_r:exported3_radio_prop:s0
+7060667000-706069d000 r-xp 00000000 fc:00 2371 /system/lib64/libopenjdk.so
+706069d000-70606b2000 ---p 00000000 00:00 0
+70606b2000-70606b4000 r--p 0003e000 fc:00 2371 /system/lib64/libopenjdk.so
+70606b4000-70606b6000 rw-p 00040000 fc:00 2371 /system/lib64/libopenjdk.so
+70606bb000-70606db000 r--s 00000000 00:10 16608 /dev/__properties__/u:object_r:exported_system_prop:s0
+70606db000-70606e3000 r-xp 00000000 fc:00 2538 /system/lib64/libopenjdkjvm.so
+70606e3000-70606fa000 ---p 00000000 00:00 0
+70606fa000-70606fb000 r--p 0000f000 fc:00 2538 /system/lib64/libopenjdkjvm.so
+70606fb000-70606fc000 rw-p 00010000 fc:00 2538 /system/lib64/libopenjdkjvm.so
+7060701000-7060722000 r--s 00000000 fc:00 227 /system/fonts/NotoSansAnatolianHieroglyphs-Regular.otf
+7060722000-7061e18000 r--s 00000000 fc:00 1350 /system/usr/icu/icudt60l.dat
+7061e18000-7061e5d000 r-xp 00000000 fc:00 2368 /system/lib64/libjavacore.so
+7061e5d000-7061e71000 ---p 00000000 00:00 0
+7061e71000-7061e73000 r--p 0004e000 fc:00 2368 /system/lib64/libjavacore.so
+7061e73000-7061e75000 rw-p 00050000 fc:00 2368 /system/lib64/libjavacore.so
+7061e75000-7061e76000 rw-p 00000000 00:00 0 [anon:.bss]
+7061e77000-7061e96000 r--s 00000000 fc:00 186 /system/fonts/NotoSansYi-Regular.ttf
+7061e96000-7061e99000 r-xp 00000000 fc:00 2953 /system/lib64/libwebviewchromium_plat_support.so
+7061e99000-7061eb5000 ---p 00000000 00:00 0
+7061eb5000-7061eb6000 r--p 0000f000 fc:00 2953 /system/lib64/libwebviewchromium_plat_support.so
+7061eb6000-7061eb7000 rw-p 00010000 fc:00 2953 /system/lib64/libwebviewchromium_plat_support.so
+7061ebc000-7061edd000 r--s 00000000 fc:00 100 /system/fonts/NotoSansBamum-Regular.ttf
+7061edd000-7061eed000 r-xp 00000000 fc:00 2945 /system/lib64/libRS.so
+7061eed000-7061efc000 ---p 00000000 00:00 0
+7061efc000-7061efd000 r--p 0000f000 fc:00 2945 /system/lib64/libRS.so
+7061efd000-7061efe000 rw-p 00010000 fc:00 2945 /system/lib64/libRS.so
+7061f05000-7061f6b000 r-xp 00000000 fc:00 2423 /system/lib64/android.hardware.renderscript@1.0.so
+7061f6b000-7061f7a000 ---p 00000000 00:00 0
+7061f7a000-7061f7f000 r--p 0006b000 fc:00 2423 /system/lib64/android.hardware.renderscript@1.0.so
+7061f7f000-7061f80000 rw-p 00070000 fc:00 2423 /system/lib64/android.hardware.renderscript@1.0.so
+7061f99000-7061f9b000 r-xp 00000000 fc:00 2614 /system/lib64/libOpenSLES.so
+7061f9b000-7061fb8000 ---p 00000000 00:00 0
+7061fb8000-7061fb9000 r--p 0000f000 fc:00 2614 /system/lib64/libOpenSLES.so
+7061fb9000-7061fba000 rw-p 00010000 fc:00 2614 /system/lib64/libOpenSLES.so
+7061fc6000-7061fc8000 r-xp 00000000 fc:00 2963 /system/lib64/libOpenMAXAL.so
+7061fc8000-7061fe5000 ---p 00000000 00:00 0
+7061fe5000-7061fe6000 r--p 0000f000 fc:00 2963 /system/lib64/libOpenMAXAL.so
+7061fe6000-7061fe7000 rw-p 00010000 fc:00 2963 /system/lib64/libOpenMAXAL.so
+7061fe7000-7062000000 r--s 00000000 fc:00 143 /system/fonts/NotoSansBhaiksuki-Regular.otf
+7062000000-7062003000 r-xp 00000000 fc:00 2447 /system/lib64/libtextclassifier_hash.so
+7062003000-706201f000 ---p 00000000 00:00 0
+706201f000-7062020000 r--p 0000f000 fc:00 2447 /system/lib64/libtextclassifier_hash.so
+7062020000-7062021000 rw-p 00010000 fc:00 2447 /system/lib64/libtextclassifier_hash.so
+7062022000-7062042000 rw-p 00000000 00:05 10269731 [anon:dalvik-CompilerMetadata]
+7062042000-7062077000 r-xp 00000000 fc:00 2372 /system/lib64/android.hardware.neuralnetworks@1.0.so
+7062077000-7062095000 ---p 00000000 00:00 0
+7062095000-706209b000 r--p 0003a000 fc:00 2372 /system/lib64/android.hardware.neuralnetworks@1.0.so
+706209b000-706209c000 rw-p 00040000 fc:00 2372 /system/lib64/android.hardware.neuralnetworks@1.0.so
+70620a9000-70620c9000 rw-p 00000000 00:05 10269730 [anon:dalvik-CompilerMetadata]
+70620c9000-70620e3000 r-xp 00000000 fc:00 2956 /system/lib64/android.hardware.neuralnetworks@1.1.so
+70620e3000-70620f4000 ---p 00000000 00:00 0
+70620f4000-70620f7000 r--p 0001d000 fc:00 2956 /system/lib64/android.hardware.neuralnetworks@1.1.so
+70620f7000-70620f8000 rw-p 00020000 fc:00 2956 /system/lib64/android.hardware.neuralnetworks@1.1.so
+706210b000-70621d0000 r-xp 00000000 fc:00 2387 /system/lib64/libneuralnetworks.so
+70621d0000-70621e3000 ---p 00000000 00:00 0
+70621e3000-70621e5000 r--p 000ce000 fc:00 2387 /system/lib64/libneuralnetworks.so
+70621e5000-70621e7000 rw-p 000d0000 fc:00 2387 /system/lib64/libneuralnetworks.so
+70621e7000-7062372000 rw-p 00000000 00:00 0 [anon:.bss]
+7062373000-7062395000 r--s 00000000 fc:00 274 /system/fonts/NotoSerifMyanmar-Bold.otf
+7062395000-7062398000 r-xp 00000000 fc:00 2937 /system/lib64/libjnigraphics.so
+7062398000-70623b4000 ---p 00000000 00:00 0
+70623b4000-70623b5000 r--p 0000f000 fc:00 2937 /system/lib64/libjnigraphics.so
+70623b5000-70623b6000 rw-p 00010000 fc:00 2937 /system/lib64/libjnigraphics.so
+70623c8000-70623e0000 r-xp 00000000 fc:00 2662 /system/lib64/libGLESv3.so
+70623e0000-70623f7000 ---p 00000000 00:00 0
+70623f7000-70623f8000 r--p 0001f000 fc:00 2662 /system/lib64/libGLESv3.so
+70623f8000-70623f9000 rw-p 00020000 fc:00 2662 /system/lib64/libGLESv3.so
+70623fc000-706241c000 rw-p 00000000 00:05 10269729 [anon:dalvik-CompilerMetadata]
+706241c000-7062444000 r-xp 00000000 fc:00 2603 /system/lib64/libexif.so
+7062444000-706245f000 ---p 00000000 00:00 0
+706245f000-7062472000 r--p 0002d000 fc:00 2603 /system/lib64/libexif.so
+7062472000-7062473000 rw-p 00040000 fc:00 2603 /system/lib64/libexif.so
+7062474000-7062490000 r--s 00000000 fc:00 286 /system/fonts/NotoSansMongolian-Regular.ttf
+7062490000-7062491000 r-xp 00000000 fc:00 2357 /system/lib64/libasyncio.so
+7062491000-70624af000 ---p 00000000 00:00 0
+70624af000-70624b0000 r--p 0000f000 fc:00 2357 /system/lib64/libasyncio.so
+70624b0000-70624b1000 rw-p 00010000 fc:00 2357 /system/lib64/libasyncio.so
+70624b5000-70624cf000 r--s 00000000 fc:00 221 /system/fonts/NotoSansMyanmarUI-Bold.ttf
+70624cf000-7062508000 r-xp 00000000 fc:00 2401 /system/lib64/libmtp.so
+7062508000-7062522000 ---p 00000000 00:00 0
+7062522000-7062525000 r--p 0003d000 fc:00 2401 /system/lib64/libmtp.so
+7062525000-706252c000 rw-p 00040000 fc:00 2401 /system/lib64/libmtp.so
+7062530000-7062550000 rw-p 00000000 00:05 10269728 [anon:dalvik-CompilerMetadata]
+7062550000-7062572000 r--s 00000000 fc:00 234 /system/fonts/NotoSerifMyanmar-Regular.otf
+7062572000-706259e000 r-xp 00000000 fc:00 2620 /system/lib64/libmediandk.so
+706259e000-70625b9000 ---p 00000000 00:00 0
+70625b9000-70625bc000 r--p 0002d000 fc:00 2620 /system/lib64/libmediandk.so
+70625bc000-70625c0000 rw-p 00030000 fc:00 2620 /system/lib64/libmediandk.so
+70625c2000-70625d1000 r-xp 00000000 fc:00 2613 /system/lib64/libmidi.so
+70625d1000-70625ef000 ---p 00000000 00:00 0
+70625ef000-70625f1000 r--p 0000e000 fc:00 2613 /system/lib64/libmidi.so
+70625f1000-70625f2000 rw-p 00010000 fc:00 2613 /system/lib64/libmidi.so
+7062600000-7062621000 r-xp 00000000 fc:00 2366 /system/lib64/libmediadrmmetrics_lite.so
+7062621000-706263d000 ---p 00000000 00:00 0
+706263d000-706263f000 r--p 0002e000 fc:00 2366 /system/lib64/libmediadrmmetrics_lite.so
+706263f000-7062640000 rw-p 00030000 fc:00 2366 /system/lib64/libmediadrmmetrics_lite.so
+706264b000-706266b000 rw-p 00000000 00:05 10269727 [anon:dalvik-CompilerMetadata]
+706266b000-70626d4000 r-xp 00000000 fc:00 2727 /system/lib64/libmedia_jni.so
+70626d4000-70626eb000 ---p 00000000 00:00 0
+70626eb000-70626f2000 r--p 00069000 fc:00 2727 /system/lib64/libmedia_jni.so
+70626f2000-70626f3000 rw-p 00070000 fc:00 2727 /system/lib64/libmedia_jni.so
+7062703000-7062732000 r-xp 00000000 fc:00 2399 /system/lib64/libcamera2ndk.so
+7062732000-7062748000 ---p 00000000 00:00 0
+7062748000-706274b000 r--p 0003d000 fc:00 2399 /system/lib64/libcamera2ndk.so
+706274b000-7062750000 rw-p 00040000 fc:00 2399 /system/lib64/libcamera2ndk.so
+7062768000-7062788000 rw-p 00000000 00:05 10269726 [anon:dalvik-CompilerMetadata]
+7062788000-70627ee000 r-xp 00000000 fc:00 2974 /system/lib64/android.hardware.drm@1.0.so
+70627ee000-7062805000 ---p 00000000 00:00 0
+7062805000-706280d000 r--p 00068000 fc:00 2974 /system/lib64/android.hardware.drm@1.0.so
+706280d000-706280e000 rw-p 00070000 fc:00 2974 /system/lib64/android.hardware.drm@1.0.so
+706281a000-706281b000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+706281b000-706281f000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+706281f000-7062843000 r--s 00000000 fc:00 142 /system/fonts/NotoSansKhmer-VF.ttf
+7062843000-7062886000 r-xp 00000000 fc:00 2637 /system/lib64/android.hardware.drm@1.1.so
+7062886000-70628a5000 ---p 00000000 00:00 0
+70628a5000-70628ab000 r--p 0004a000 fc:00 2637 /system/lib64/android.hardware.drm@1.1.so
+70628ab000-70628ac000 rw-p 00050000 fc:00 2637 /system/lib64/android.hardware.drm@1.1.so
+70628b0000-70628b1000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70628b1000-70628b5000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70628b5000-70628db000 r--s 00000000 fc:00 137 /system/fonts/NotoSansSinhala-Bold.ttf
+70628db000-7062907000 r-xp 00000000 fc:00 2478 /system/lib64/libmediadrm.so
+7062907000-7062918000 ---p 00000000 00:00 0
+7062918000-7062920000 r--p 00038000 fc:00 2478 /system/lib64/libmediadrm.so
+7062920000-7062921000 rw-p 00040000 fc:00 2478 /system/lib64/libmediadrm.so
+7062922000-7062929000 rw-p 00000000 fc:00 583 /system/etc/event-log-tags
+7062929000-7062951000 r--s 00000000 fc:00 296 /system/fonts/NotoSansSinhala-Regular.ttf
+7062951000-7062997000 r-xp 00000000 fc:00 2448 /system/lib64/libaaudio.so
+7062997000-70629ac000 ---p 00000000 00:00 0
+70629ac000-70629b2000 r--p 0004a000 fc:00 2448 /system/lib64/libaaudio.so
+70629b2000-70629ba000 rw-p 00050000 fc:00 2448 /system/lib64/libaaudio.so
+70629ba000-70629bb000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70629bb000-70629bf000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70629bf000-70629c0000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629c0000-70629c3000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70629c3000-70629c4000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629c4000-70629c5000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70629c5000-70629c9000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70629c9000-70629e3000 r-xp 00000000 fc:00 2940 /system/lib64/libandroid.so
+70629e3000-70629f3000 ---p 00000000 00:00 0
+70629f3000-70629f6000 r--p 0001d000 fc:00 2940 /system/lib64/libandroid.so
+70629f6000-70629f7000 rw-p 00020000 fc:00 2940 /system/lib64/libandroid.so
+70629f8000-70629f9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629f9000-70629fc000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70629fc000-70629fd000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629fd000-7062a3e000 r--s 00000000 fc:00 216 /system/fonts/NotoSerif-BoldItalic.ttf
+7062a3e000-7062b06000 rw-p 00000000 00:05 10270984 [anon:dalvik-indirect ref table]
+7062b06000-7062bce000 rw-p 00000000 00:05 10270983 [anon:dalvik-indirect ref table]
+7062bce000-7062dce000 rw-p 00000000 00:05 10270726 [anon:dalvik-rb copying gc mark stack]
+7062dce000-70635ce000 rw-p 00000000 00:05 10270725 [anon:dalvik-concurrent copying gc mark stack]
+70635ce000-7063dcf000 rw-p 00000000 00:05 10270724 [anon:dalvik-live stack]
+7063dcf000-70645d0000 rw-p 00000000 00:05 10270723 [anon:dalvik-allocation stack]
+70645d0000-70649d1000 rw-p 00000000 00:05 10270721 [anon:dalvik-card table]
+70649d1000-7064ad1000 rw-p 00000000 00:05 10267648 [anon:dalvik-large object free list space allocation info map]
+7064ad1000-7065ad1000 rw-p 00000000 00:05 10267644 [anon:dalvik-region space live bitmap]
+7065ad1000-7065bd1000 rw-p 00000000 00:05 10267642 [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+7065bd1000-7065cd1000 rw-p 00000000 00:05 10267641 [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
+7065cd1000-7065cd2000 r-xp 00000000 fc:00 2946 /system/lib64/libsigchain.so
+7065cd2000-7065cf0000 ---p 00000000 00:00 0
+7065cf0000-7065cf1000 r--p 0000f000 fc:00 2946 /system/lib64/libsigchain.so
+7065cf1000-7065cf2000 rw-p 00010000 fc:00 2946 /system/lib64/libsigchain.so
+7065cf4000-7065d0f000 r--s 00000000 fc:00 190 /system/fonts/NotoSansMyanmar-Bold.ttf
+7065d0f000-7065d22000 r-xp 00000000 fc:00 2405 /system/lib64/liblz4.so
+7065d22000-7065d3e000 ---p 00000000 00:00 0
+7065d3e000-7065d3f000 r--p 0001f000 fc:00 2405 /system/lib64/liblz4.so
+7065d3f000-7065d40000 rw-p 00020000 fc:00 2405 /system/lib64/liblz4.so
+7065d40000-7065d5a000 r--s 00000000 fc:00 222 /system/fonts/NotoSansMyanmarUI-Regular.ttf
+7065d5a000-7065d5e000 r-xp 00000000 fc:00 2609 /system/lib64/libtombstoned_client.so
+7065d5e000-7065d79000 ---p 00000000 00:00 0
+7065d79000-7065d7a000 r--p 0000f000 fc:00 2609 /system/lib64/libtombstoned_client.so
+7065d7a000-7065d7b000 rw-p 00010000 fc:00 2609 /system/lib64/libtombstoned_client.so
+7065d7f000-7065d80000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+7065d80000-7065d84000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+7065d84000-706636e000 r-xp 00000000 fc:00 2671 /system/lib64/libart.so
+706636e000-706638d000 ---p 00000000 00:00 0
+706638d000-706639e000 r--p 005ef000 fc:00 2671 /system/lib64/libart.so
+706639e000-70663a1000 rw-p 00600000 fc:00 2671 /system/lib64/libart.so
+70663a1000-70663a4000 rw-p 00000000 00:00 0 [anon:.bss]
+70663a6000-70663c6000 rw-p 00000000 00:05 10269725 [anon:dalvik-CompilerMetadata]
+70663c6000-70663c8000 r-xp 00000000 fc:00 2673 /system/lib64/libmetricslogger.so
+70663c8000-70663e5000 ---p 00000000 00:00 0
+70663e5000-70663e6000 r--p 0000f000 fc:00 2673 /system/lib64/libmetricslogger.so
+70663e6000-70663e7000 rw-p 00010000 fc:00 2673 /system/lib64/libmetricslogger.so
+70663e7000-7066400000 r--s 00000000 fc:00 110 /system/fonts/NotoSansLepcha-Regular.ttf
+7066400000-7066800000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+7066803000-706681e000 r--s 00000000 fc:00 297 /system/fonts/NotoSansMyanmar-Regular.ttf
+706681e000-7066821000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066821000-7066822000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066822000-7066b1d000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066b1d000-7066b1e000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066b1e000-7066ba0000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba0000-7066ba1000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba1000-7066ba2000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba2000-7066ba5000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba5000-7066ba6000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba6000-70e681e000 r--p 00000000 00:00 0 [anon:cfi shadow]
+70e681e000-70e6854000 r-xp 00000000 fc:00 2431 /system/lib64/libstagefright_foundation.so
+70e6854000-70e6865000 ---p 00000000 00:00 0
+70e6865000-70e6867000 r--p 0003e000 fc:00 2431 /system/lib64/libstagefright_foundation.so
+70e6867000-70e686c000 rw-p 00040000 fc:00 2431 /system/lib64/libstagefright_foundation.so
+70e686d000-70e686e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e686e000-70e6871000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6871000-70e6873000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6873000-70e6876000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6876000-70e6877000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6877000-70e688c000 r--s 00000000 fc:00 301 /system/fonts/NotoSansSinhalaUI-Bold.otf
+70e688c000-70e688e000 r-xp 00000000 fc:00 2943 /system/lib64/libion.so
+70e688e000-70e68ab000 ---p 00000000 00:00 0
+70e68ab000-70e68ac000 r--p 0000f000 fc:00 2943 /system/lib64/libion.so
+70e68ac000-70e68ad000 rw-p 00010000 fc:00 2943 /system/lib64/libion.so
+70e68ad000-70e68af000 rw-p 00000000 00:05 10282496 [anon:dalvik-indirect ref table]
+70e68af000-70e68b1000 rw-p 00000000 00:05 10282493 [anon:dalvik-indirect ref table]
+70e68b1000-70e68ee000 r--s 00000000 fc:00 256 /system/fonts/NotoSerif-Italic.ttf
+70e68ee000-70e6910000 r-xp 00000000 fc:00 2502 /system/lib64/libhidlbase.so
+70e6910000-70e692c000 ---p 00000000 00:00 0
+70e692c000-70e692e000 r--p 0002e000 fc:00 2502 /system/lib64/libhidlbase.so
+70e692e000-70e692f000 rw-p 00030000 fc:00 2502 /system/lib64/libhidlbase.so
+70e6930000-70e693f000 r--s 00000000 fc:00 1082 /system/usr/hyphen-data/hyph-en-us.hyb
+70e693f000-70e6954000 r--s 00000000 fc:00 138 /system/fonts/NotoSansSinhalaUI-Regular.otf
+70e6954000-70e6978000 r-xp 00000000 fc:00 2482 /system/lib64/libui.so
+70e6978000-70e6992000 ---p 00000000 00:00 0
+70e6992000-70e6994000 r--p 0002e000 fc:00 2482 /system/lib64/libui.so
+70e6994000-70e6995000 rw-p 00030000 fc:00 2482 /system/lib64/libui.so
+70e6996000-70e69a2000 r--s 00000000 fc:00 1117 /system/usr/hyphen-data/hyph-en-gb.hyb
+70e69a2000-70e69b7000 r--s 00000000 fc:00 202 /system/fonts/NotoSerifSinhala-Bold.otf
+70e69b7000-70e69cb000 r--s 00000000 fc:00 124 /system/fonts/NotoSansOriyaUI-Bold.ttf
+70e69cb000-70e69e1000 r-xp 00000000 fc:00 2537 /system/lib64/liblog.so
+70e69e1000-70e69fa000 ---p 00000000 00:00 0
+70e69fa000-70e69fb000 r--p 0001f000 fc:00 2537 /system/lib64/liblog.so
+70e69fb000-70e69fc000 rw-p 00020000 fc:00 2537 /system/lib64/liblog.so
+70e69fc000-70e69fe000 rw-p 00000000 00:05 10266158 [anon:dalvik-indirect ref table]
+70e69fe000-70e69ff000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e69ff000-70e6a02000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6a02000-70e6a03000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6a03000-70e6a05000 r-xp 00000000 fc:00 2489 /system/lib64/android.hidl.token@1.0-utils.so
+70e6a05000-70e6a22000 ---p 00000000 00:00 0
+70e6a22000-70e6a23000 r--p 0000f000 fc:00 2489 /system/lib64/android.hidl.token@1.0-utils.so
+70e6a23000-70e6a24000 rw-p 00010000 fc:00 2489 /system/lib64/android.hidl.token@1.0-utils.so
+70e6a25000-70e6a2e000 r--s 00000000 fc:00 1120 /system/usr/hyphen-data/hyph-ga.hyb
+70e6a2e000-70e6a42000 r--s 00000000 fc:00 109 /system/fonts/NotoSansOriyaUI-Regular.ttf
+70e6a42000-70e6a59000 r-xp 00000000 fc:00 2446 /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a59000-70e6a6e000 ---p 00000000 00:00 0
+70e6a6e000-70e6a70000 r--p 0001e000 fc:00 2446 /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a70000-70e6a71000 rw-p 00020000 fc:00 2446 /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a72000-70e6a78000 r--s 00000000 fc:00 1084 /system/usr/hyphen-data/hyph-et.hyb
+70e6a78000-70e6a9d000 r--s 00000000 fc:00 207 /system/fonts/NotoSerifTelugu-Bold.ttf
+70e6a9d000-70e6a9f000 r-xp 00000000 fc:00 2330 /system/lib64/android.hardware.configstore-utils.so
+70e6a9f000-70e6abc000 ---p 00000000 00:00 0
+70e6abc000-70e6abd000 r--p 0000f000 fc:00 2330 /system/lib64/android.hardware.configstore-utils.so
+70e6abd000-70e6abe000 rw-p 00010000 fc:00 2330 /system/lib64/android.hardware.configstore-utils.so
+70e6abe000-70e6ac0000 r--s f8042000 00:10 20630 /dev/kgsl-3d0
+70e6ac0000-70e6adc000 r--s 00000000 fc:00 172 /system/fonts/NotoSansTeluguUI-Bold.ttf
+70e6adc000-70e6ae0000 r-xp 00000000 fc:00 2555 /system/lib64/libstagefright_omx_utils.so
+70e6ae0000-70e6afb000 ---p 00000000 00:00 0
+70e6afb000-70e6afc000 r--p 0000f000 fc:00 2555 /system/lib64/libstagefright_omx_utils.so
+70e6afc000-70e6afd000 rw-p 00010000 fc:00 2555 /system/lib64/libstagefright_omx_utils.so
+70e6afd000-70e6afe000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70e6afe000-70e6b02000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70e6b02000-70e6b27000 r--s 00000000 fc:00 271 /system/fonts/NotoSerifTelugu-Regular.ttf
+70e6b27000-70e6b61000 r-xp 00000000 fc:00 2695 /system/lib64/libdexfile.so
+70e6b61000-70e6b73000 ---p 00000000 00:00 0
+70e6b73000-70e6b75000 r--p 0003e000 fc:00 2695 /system/lib64/libdexfile.so
+70e6b75000-70e6b76000 rw-p 00040000 fc:00 2695 /system/lib64/libdexfile.so
+70e6b76000-70e6b78000 rw-p 00000000 00:05 10253452 [anon:dalvik-indirect ref table]
+70e6b78000-70e6b85000 r--s 00000000 fc:00 1080 /system/usr/hyphen-data/hyph-cu.hyb
+70e6b85000-70e6b96000 r-xp 00000000 fc:00 2957 /system/lib64/libaudioutils.so
+70e6b96000-70e6bb4000 ---p 00000000 00:00 0
+70e6bb4000-70e6bb5000 r--p 0001f000 fc:00 2957 /system/lib64/libaudioutils.so
+70e6bb5000-70e6bb6000 rw-p 00020000 fc:00 2957 /system/lib64/libaudioutils.so
+70e6bb6000-70e6bb7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6bb7000-70e6bba000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6bba000-70e6bbb000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6bbb000-70e6bd7000 r--s 00000000 fc:00 132 /system/fonts/NotoSansTeluguUI-Regular.ttf
+70e6bd7000-70e6bdc000 r-xp 00000000 fc:00 2409 /system/lib64/libprocessgroup.so
+70e6bdc000-70e6bf6000 ---p 00000000 00:00 0
+70e6bf6000-70e6bf7000 r--p 0000f000 fc:00 2409 /system/lib64/libprocessgroup.so
+70e6bf7000-70e6bf8000 rw-p 00010000 fc:00 2409 /system/lib64/libprocessgroup.so
+70e6bf8000-70e6c09000 r--s 00000000 fc:00 79 /system/fonts/NotoSansNewa-Regular.otf
+70e6c09000-70e6c1c000 r-xp 00000000 fc:00 2329 /system/lib64/android.hidl.memory.token@1.0.so
+70e6c1c000-70e6c36000 ---p 00000000 00:00 0
+70e6c36000-70e6c38000 r--p 0001e000 fc:00 2329 /system/lib64/android.hidl.memory.token@1.0.so
+70e6c38000-70e6c39000 rw-p 00020000 fc:00 2329 /system/lib64/android.hidl.memory.token@1.0.so
+70e6c3a000-70e6c4f000 r--s 00000000 fc:00 253 /system/fonts/NotoSansOriya-Bold.ttf
+70e6c4f000-70e6c6b000 r-xp 00000000 fc:00 2407 /system/lib64/libutils.so
+70e6c6b000-70e6c7e000 ---p 00000000 00:00 0
+70e6c7e000-70e6c7f000 r--p 0001f000 fc:00 2407 /system/lib64/libutils.so
+70e6c7f000-70e6c80000 rw-p 00020000 fc:00 2407 /system/lib64/libutils.so
+70e6c80000-70e6c9d000 r-xp 00000000 fc:00 2934 /system/lib64/libtinyxml2.so
+70e6c9d000-70e6cba000 ---p 00000000 00:00 0
+70e6cba000-70e6cbc000 r--p 0001e000 fc:00 2934 /system/lib64/libtinyxml2.so
+70e6cbc000-70e6cbf000 rw-p 00020000 fc:00 2934 /system/lib64/libtinyxml2.so
+70e6cbf000-70e6ccf000 r--s 00000000 fc:00 80 /system/fonts/NotoSansMarchen-Regular.otf
+70e6ccf000-70e6ce0000 r-xp 00000000 fc:00 2655 /system/lib64/libbase.so
+70e6ce0000-70e6cfe000 ---p 00000000 00:00 0
+70e6cfe000-70e6cff000 r--p 0001f000 fc:00 2655 /system/lib64/libbase.so
+70e6cff000-70e6d00000 rw-p 00020000 fc:00 2655 /system/lib64/libbase.so
+70e6d00000-70e6d09000 r--s 00000000 fc:00 1113 /system/usr/hyphen-data/hyph-cy.hyb
+70e6d09000-70e6d50000 r-xp 00000000 fc:00 2495 /system/lib64/libRScpp.so
+70e6d50000-70e6d68000 ---p 00000000 00:00 0
+70e6d68000-70e6d69000 r--p 0004f000 fc:00 2495 /system/lib64/libRScpp.so
+70e6d69000-70e6d6a000 rw-p 00050000 fc:00 2495 /system/lib64/libRScpp.so
+70e6d6b000-70e6d6d000 r--s 00088000 103:1d 1736830 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk
+70e6d6d000-70e6d7d000 r--s 00000000 fc:00 238 /system/fonts/NotoSansVai-Regular.ttf
+70e6d7d000-70e6d98000 r--s 00000000 fc:00 276 /system/fonts/NotoSansTelugu-Bold.ttf
+70e6d98000-70e6f2b000 r-xp 00000000 fc:00 2961 /system/lib64/libicuuc.so
+70e6f2b000-70e6f47000 ---p 00000000 00:00 0
+70e6f47000-70e6f5c000 r--p 0019b000 fc:00 2961 /system/lib64/libicuuc.so
+70e6f5c000-70e6f5d000 rw-p 001b0000 fc:00 2961 /system/lib64/libicuuc.so
+70e6f5d000-70e6f5e000 rw-p 00000000 00:00 0 [anon:.bss]
+70e6f5f000-70e6f68000 r--s 00000000 fc:00 159 /system/fonts/NotoSansLinearA-Regular.otf
+70e6f68000-70e6f84000 r--s 00000000 fc:00 170 /system/fonts/NotoSansTelugu-Regular.ttf
+70e6f84000-70e7058000 r-xp 00000000 fc:00 2356 /system/lib64/libc.so
+70e7058000-70e706e000 ---p 00000000 00:00 0
+70e706e000-70e7074000 r--p 000da000 fc:00 2356 /system/lib64/libc.so
+70e7074000-70e7076000 rw-p 000e0000 fc:00 2356 /system/lib64/libc.so
+70e7076000-70e7077000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7077000-70e7078000 r--p 00000000 00:00 0 [anon:.bss]
+70e7078000-70e7080000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7080000-70e7087000 r--s 00000000 fc:00 102 /system/fonts/NotoSansSharada-Regular.otf
+70e7087000-70e708e000 r-xp 00000000 fc:00 2378 /system/lib64/libheif.so
+70e708e000-70e70a4000 ---p 00000000 00:00 0
+70e70a4000-70e70a6000 r--p 0000e000 fc:00 2378 /system/lib64/libheif.so
+70e70a6000-70e70a7000 rw-p 00010000 fc:00 2378 /system/lib64/libheif.so
+70e70a7000-70e70a9000 r--s 00000000 fc:00 1116 /system/usr/hyphen-data/hyph-sl.hyb
+70e70a9000-70e70ab000 r--s 00000000 fc:00 1147 /system/usr/hyphen-data/hyph-mn-cyrl.hyb
+70e70ab000-70e70c5000 r--s 00000000 fc:00 291 /system/fonts/NotoSansBengaliUI-Bold.ttf
+70e70c5000-70e70ea000 r-xp 00000000 fc:00 2545 /system/lib64/libEGL.so
+70e70ea000-70e7109000 ---p 00000000 00:00 0
+70e7109000-70e710d000 r--p 0002c000 fc:00 2545 /system/lib64/libEGL.so
+70e710d000-70e710e000 rw-p 00030000 fc:00 2545 /system/lib64/libEGL.so
+70e710e000-70e7115000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7115000-70e7119000 r--s 00000000 fc:00 1143 /system/usr/hyphen-data/hyph-es.hyb
+70e7119000-70e712e000 r--s 00000000 fc:00 71 /system/fonts/NotoSansOriya-Regular.ttf
+70e712e000-70e717a000 r--s 00000000 fc:00 272 /system/fonts/Roboto-Thin.ttf
+70e717a000-70e71db000 r-xp 00000000 fc:00 2393 /system/lib64/libpdx_default_transport.so
+70e71db000-70e71f7000 ---p 00000000 00:00 0
+70e71f7000-70e71f9000 r--p 0006e000 fc:00 2393 /system/lib64/libpdx_default_transport.so
+70e71f9000-70e71fa000 rw-p 00070000 fc:00 2393 /system/lib64/libpdx_default_transport.so
+70e71fa000-70e71fb000 rw-p 00000000 00:00 0 [anon:.bss]
+70e71fc000-70e71fe000 r--s 00000000 fc:00 1107 /system/usr/hyphen-data/hyph-fr.hyb
+70e71fe000-70e7200000 r--s 00000000 fc:00 1097 /system/usr/hyphen-data/hyph-da.hyb
+70e7200000-70e7223000 r-xp 00000000 fc:00 2380 /system/lib64/libminikin.so
+70e7223000-70e723e000 ---p 00000000 00:00 0
+70e723e000-70e723f000 r--p 0002f000 fc:00 2380 /system/lib64/libminikin.so
+70e723f000-70e7240000 rw-p 00030000 fc:00 2380 /system/lib64/libminikin.so
+70e7241000-70e724d000 r--s 00000000 fc:00 179 /system/fonts/NotoSansTaiTham-Regular.ttf
+70e724d000-70e725c000 r-xp 00000000 fc:00 2527 /system/lib64/libmediautils.so
+70e725c000-70e7279000 ---p 00000000 00:00 0
+70e7279000-70e727b000 r--p 0001e000 fc:00 2527 /system/lib64/libmediautils.so
+70e727b000-70e727c000 rw-p 00020000 fc:00 2527 /system/lib64/libmediautils.so
+70e727d000-70e7283000 r--s 00000000 fc:00 136 /system/fonts/NotoSansMiao-Regular.otf
+70e7283000-70e74d2000 r-xp 00000000 fc:00 2349 /system/lib64/libicui18n.so
+70e74d2000-70e74e6000 ---p 00000000 00:00 0
+70e74e6000-70e74fa000 r--p 0025c000 fc:00 2349 /system/lib64/libicui18n.so
+70e74fa000-70e74fb000 rw-p 00270000 fc:00 2349 /system/lib64/libicui18n.so
+70e74fc000-70e74ff000 r--s 00000000 103:1d 1474562 /data/resource-cache/vendor@overlay@framework-res__auto_generated_rro.apk@idmap
+70e74ff000-70e750c000 r--s 00000000 fc:00 126 /system/fonts/NotoSansSyriacWestern-Regular.ttf
+70e750c000-70e751b000 r-xp 00000000 fc:00 2466 /system/lib64/libmediaextractor.so
+70e751b000-70e753a000 ---p 00000000 00:00 0
+70e753a000-70e753b000 r--p 0000f000 fc:00 2466 /system/lib64/libmediaextractor.so
+70e753b000-70e753c000 rw-p 00010000 fc:00 2466 /system/lib64/libmediaextractor.so
+70e753d000-70e7540000 r--s 00000000 fc:00 107 /system/fonts/NotoSansPauCinHau-Regular.otf
+70e7540000-70e7593000 r-xp 00000000 fc:00 2363 /system/lib64/libandroidfw.so
+70e7593000-70e75ac000 ---p 00000000 00:00 0
+70e75ac000-70e75af000 r--p 0005d000 fc:00 2363 /system/lib64/libandroidfw.so
+70e75af000-70e75b0000 rw-p 00060000 fc:00 2363 /system/lib64/libandroidfw.so
+70e75b0000-70e75b2000 r--s 00000000 fc:00 1083 /system/usr/hyphen-data/hyph-be.hyb
+70e75b2000-70e75cd000 r--s 00000000 fc:00 270 /system/fonts/NotoSansBengaliUI-Regular.ttf
+70e75cd000-70e75cf000 r-xp 00000000 fc:00 2701 /system/lib64/libmemtrack.so
+70e75cf000-70e75ec000 ---p 00000000 00:00 0
+70e75ec000-70e75ed000 r--p 0000f000 fc:00 2701 /system/lib64/libmemtrack.so
+70e75ed000-70e75ee000 rw-p 00010000 fc:00 2701 /system/lib64/libmemtrack.so
+70e75ee000-70e75f0000 r--s 00000000 fc:00 209 /system/fonts/NotoSansSoraSompeng-Regular.otf
+70e75f0000-70e760d000 r--s 00000000 fc:00 243 /system/fonts/NotoSerifBengali-Bold.ttf
+70e760d000-70e7613000 r-xp 00000000 fc:00 2667 /system/lib64/libutilscallstack.so
+70e7613000-70e762c000 ---p 00000000 00:00 0
+70e762c000-70e762d000 r--p 0000f000 fc:00 2667 /system/lib64/libutilscallstack.so
+70e762d000-70e762e000 rw-p 00010000 fc:00 2667 /system/lib64/libutilscallstack.so
+70e762e000-70e7632000 r--s 00000000 fc:00 99 /system/fonts/NotoSansPahawhHmong-Regular.otf
+70e7632000-70e764f000 r--s 00000000 fc:00 205 /system/fonts/NotoSerifBengali-Regular.ttf
+70e764f000-70e7661000 r-xp 00000000 fc:00 2710 /system/lib64/libcutils.so
+70e7661000-70e767d000 ---p 00000000 00:00 0
+70e767d000-70e767f000 r--p 0001e000 fc:00 2710 /system/lib64/libcutils.so
+70e767f000-70e7680000 rw-p 00020000 fc:00 2710 /system/lib64/libcutils.so
+70e7680000-70e7683000 r--s 00000000 fc:00 257 /system/fonts/NotoSansPalmyrene-Regular.otf
+70e7683000-70e7697000 r--s 00000000 fc:00 78 /system/fonts/NotoSansKannadaUI-Bold.ttf
+70e7697000-70e769a000 r-xp 00000000 fc:00 2362 /system/lib64/libstagefright_http_support.so
+70e769a000-70e76b6000 ---p 00000000 00:00 0
+70e76b6000-70e76b7000 r--p 0000f000 fc:00 2362 /system/lib64/libstagefright_http_support.so
+70e76b7000-70e76b8000 rw-p 00010000 fc:00 2362 /system/lib64/libstagefright_http_support.so
+70e76b8000-70e76b9000 rw-p 00000000 00:00 0 [anon:linker_alloc_lob]
+70e76b9000-70e76be000 r--s 00000000 fc:00 165 /system/fonts/NotoSansMeroitic-Regular.otf
+70e76be000-70e76cb000 r--s 00000000 fc:00 112 /system/fonts/NotoSansSyriacEastern-Regular.ttf
+70e76cb000-70e76de000 r-xp 00000000 fc:00 2343 /system/lib64/libsensor.so
+70e76de000-70e76f5000 ---p 00000000 00:00 0
+70e76f5000-70e76f8000 r--p 0001d000 fc:00 2343 /system/lib64/libsensor.so
+70e76f8000-70e76f9000 rw-p 00020000 fc:00 2343 /system/lib64/libsensor.so
+70e76f9000-70e76fc000 r--s 00000000 fc:00 157 /system/fonts/NotoSansOldPermic-Regular.otf
+70e76fc000-70e7708000 r--s 00000000 fc:00 189 /system/fonts/NotoSansSyriacEstrangela-Regular.ttf
+70e7708000-70e771d000 r-xp 00000000 fc:00 2339 /system/lib64/android.hidl.token@1.0.so
+70e771d000-70e7735000 ---p 00000000 00:00 0
+70e7735000-70e7737000 r--p 0001e000 fc:00 2339 /system/lib64/android.hidl.token@1.0.so
+70e7737000-70e7738000 rw-p 00020000 fc:00 2339 /system/lib64/android.hidl.token@1.0.so
+70e7738000-70e7739000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e7739000-70e773b000 r--s 00000000 fc:00 267 /system/fonts/NotoSansOldNorthArabian-Regular.otf
+70e773b000-70e7740000 r--s 00000000 fc:00 208 /system/fonts/NotoSansManichaean-Regular.otf
+70e7740000-70e775c000 r--s 00000000 fc:00 118 /system/fonts/NotoSansGujaratiUI-Bold.ttf
+70e775c000-70e7767000 r-xp 00000000 fc:00 2525 /system/lib64/libappfuse.so
+70e7767000-70e777b000 ---p 00000000 00:00 0
+70e777b000-70e777c000 r--p 0000f000 fc:00 2525 /system/lib64/libappfuse.so
+70e777c000-70e777d000 rw-p 00010000 fc:00 2525 /system/lib64/libappfuse.so
+70e777e000-70e7795000 r--s 00000000 fc:00 250 /system/fonts/NotoSerifKannada-Bold.ttf
+70e7795000-70e7798000 r-xp 00000000 fc:00 2413 /system/lib64/libpackagelistparser.so
+70e7798000-70e77b4000 ---p 00000000 00:00 0
+70e77b4000-70e77b5000 r--p 0000f000 fc:00 2413 /system/lib64/libpackagelistparser.so
+70e77b5000-70e77b6000 rw-p 00010000 fc:00 2413 /system/lib64/libpackagelistparser.so
+70e77b6000-70e77b8000 r--s 00000000 fc:00 147 /system/fonts/NotoSansNabataean-Regular.otf
+70e77b8000-70e77ba000 r--s 00000000 fc:00 146 /system/fonts/NotoSansMultani-Regular.otf
+70e77ba000-70e77c1000 r--s 00000000 fc:00 214 /system/fonts/NotoSansPhagsPa-Regular.ttf
+70e77c1000-70e77de000 r--s 00000000 fc:00 90 /system/fonts/NotoSansGujaratiUI-Regular.ttf
+70e77de000-70e77e0000 r-xp 00000000 fc:00 2430 /system/lib64/libdl.so
+70e77e0000-70e77fd000 ---p 00000000 00:00 0
+70e77fd000-70e77fe000 r--p 0000f000 fc:00 2430 /system/lib64/libdl.so
+70e77fe000-70e77ff000 r--p 00000000 00:00 0 [anon:.bss]
+70e7800000-70e7809000 r--s 00000000 fc:00 258 /system/fonts/NotoSansSymbols-Regular-Subsetted2.ttf
+70e7809000-70e7857000 r-xp 00000000 fc:00 2560 /system/lib64/libjpeg.so
+70e7857000-70e7868000 ---p 00000000 00:00 0
+70e7868000-70e7869000 r--p 0004f000 fc:00 2560 /system/lib64/libjpeg.so
+70e7869000-70e786a000 rw-p 00050000 fc:00 2560 /system/lib64/libjpeg.so
+70e786a000-70e7887000 r--s 00000000 fc:00 237 /system/fonts/NotoSansGujarati-Bold.ttf
+70e7887000-70e788a000 r-xp 00000000 fc:00 2608 /system/lib64/libnetd_client.so
+70e788a000-70e78a6000 ---p 00000000 00:00 0
+70e78a6000-70e78a7000 r--p 0000f000 fc:00 2608 /system/lib64/libnetd_client.so
+70e78a7000-70e78a8000 rw-p 00010000 fc:00 2608 /system/lib64/libnetd_client.so
+70e78a8000-70e78aa000 r--s 00000000 fc:00 290 /system/fonts/NotoSansMro-Regular.otf
+70e78aa000-70e78b9000 r--s 00000000 fc:00 84 /system/fonts/NotoSansLinearB-Regular.ttf
+70e78b9000-70e78d7000 r--s 00000000 fc:00 287 /system/fonts/NotoSansGujarati-Regular.ttf
+70e78d7000-70e78d8000 r-xp 00000000 fc:00 2651 /system/lib64/libvndksupport.so
+70e78d8000-70e78f6000 ---p 00000000 00:00 0
+70e78f6000-70e78f7000 r--p 0000f000 fc:00 2651 /system/lib64/libvndksupport.so
+70e78f7000-70e78f8000 rw-p 00010000 fc:00 2651 /system/lib64/libvndksupport.so
+70e78f9000-70e78fc000 r--s 00000000 fc:00 178 /system/fonts/NotoSansTaiLe-Regular.ttf
+70e78fc000-70e7900000 r--s 00000000 fc:00 163 /system/fonts/NotoSansTifinagh-Regular.ttf
+70e7900000-70e7906000 r-xp 00000000 fc:00 2659 /system/lib64/libnativeloader.so
+70e7906000-70e791f000 ---p 00000000 00:00 0
+70e791f000-70e7920000 r--p 0000f000 fc:00 2659 /system/lib64/libnativeloader.so
+70e7920000-70e7921000 rw-p 00010000 fc:00 2659 /system/lib64/libnativeloader.so
+70e7921000-70e7923000 r--s 00000000 fc:00 268 /system/fonts/NotoSansHatran-Regular.otf
+70e7923000-70e7927000 r--s 00000000 fc:00 195 /system/fonts/NotoSansTaiViet-Regular.ttf
+70e7927000-70e7945000 r--s 00000000 fc:00 139 /system/fonts/NotoSansDevanagariUI-Bold.ttf
+70e7945000-70e79a3000 r-xp 00000000 fc:00 2450 /system/lib64/libharfbuzz_ng.so
+70e79a3000-70e79b3000 ---p 00000000 00:00 0
+70e79b3000-70e79b5000 r--p 0005e000 fc:00 2450 /system/lib64/libharfbuzz_ng.so
+70e79b5000-70e79b6000 rw-p 00060000 fc:00 2450 /system/lib64/libharfbuzz_ng.so
+70e79b6000-70e79c5000 r--s 00000000 fc:00 111 /system/fonts/NotoSansKaithi-Regular.ttf
+70e79c5000-70e7a92000 r-xp 00000000 fc:00 2332 /system/lib64/libc++.so
+70e7a92000-70e7aae000 ---p 00000000 00:00 0
+70e7aae000-70e7ab8000 r--p 000d6000 fc:00 2332 /system/lib64/libc++.so
+70e7ab8000-70e7ab9000 rw-p 000e0000 fc:00 2332 /system/lib64/libc++.so
+70e7ab9000-70e7abc000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7abc000-70e7abe000 r--s 00000000 fc:00 73 /system/fonts/NotoSansBassaVah-Regular.otf
+70e7abe000-70e7ac8000 r--s 00000000 fc:00 254 /system/fonts/NotoSansJavanese-Regular.ttf
+70e7ac8000-70e7acb000 r-xp 00000000 fc:00 2660 /system/lib64/libnativebridge.so
+70e7acb000-70e7ae7000 ---p 00000000 00:00 0
+70e7ae7000-70e7ae8000 r--p 0000f000 fc:00 2660 /system/lib64/libnativebridge.so
+70e7ae8000-70e7ae9000 rw-p 00010000 fc:00 2660 /system/lib64/libnativebridge.so
+70e7ae9000-70e7aee000 r--s 00000000 fc:00 158 /system/fonts/NotoSansSaurashtra-Regular.ttf
+70e7aee000-70e7b0f000 r--s 00000000 fc:00 82 /system/fonts/NotoSansDevanagari-Bold.ttf
+70e7b0f000-70e7b2e000 r-xp 00000000 fc:00 2612 /system/lib64/libpcre2.so
+70e7b2e000-70e7b3e000 ---p 00000000 00:00 0
+70e7b3e000-70e7b3f000 r--p 0001f000 fc:00 2612 /system/lib64/libpcre2.so
+70e7b3f000-70e7b40000 rw-p 00020000 fc:00 2612 /system/lib64/libpcre2.so
+70e7b40000-70e7bcc000 r-xp 00000000 fc:00 2975 /system/lib64/libgui.so
+70e7bcc000-70e7be2000 ---p 00000000 00:00 0
+70e7be2000-70e7bf5000 r--p 0008d000 fc:00 2975 /system/lib64/libgui.so
+70e7bf5000-70e7bf6000 rw-p 000a0000 fc:00 2975 /system/lib64/libgui.so
+70e7bf6000-70e7bf7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e7bf7000-70e7bf9000 r--s 00000000 fc:00 228 /system/fonts/NotoSansUgaritic-Regular.ttf
+70e7bf9000-70e7c08000 r--s 00000000 fc:00 89 /system/fonts/NotoSansCherokee-Regular.ttf
+70e7c08000-70e7dea000 r-xp 00000000 fc:00 2441 /system/lib64/libandroid_runtime.so
+70e7dea000-70e7e04000 ---p 00000000 00:00 0
+70e7e04000-70e7e23000 r--p 001e1000 fc:00 2441 /system/lib64/libandroid_runtime.so
+70e7e23000-70e7e24000 rw-p 00200000 fc:00 2441 /system/lib64/libandroid_runtime.so
+70e7e24000-70e7e28000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7e29000-70e7e66000 r--s 00000000 fc:00 74 /system/fonts/NotoSerif-Bold.ttf
+70e7e66000-70e7ed0000 r-xp 00000000 fc:00 2547 /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ed0000-70e7edf000 ---p 00000000 00:00 0
+70e7edf000-70e7ee1000 r--p 00069000 fc:00 2547 /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ee1000-70e7ee4000 rw-p 0006b000 fc:00 2547 /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ee4000-70e89f6000 rw-p 00000000 00:00 0 [anon:.bss]
+70e89f6000-70e89fa000 r--s 00000000 fc:00 127 /system/fonts/NotoSansSylotiNagri-Regular.ttf
+70e89fa000-70e8a06000 r--s 00000000 fc:00 93 /system/fonts/NotoSansCanadianAboriginal-Regular.ttf
+70e8a06000-70e8a1b000 r-xp 00000000 fc:00 2460 /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a1b000-70e8a33000 ---p 00000000 00:00 0
+70e8a33000-70e8a35000 r--p 0001e000 fc:00 2460 /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a35000-70e8a36000 rw-p 00020000 fc:00 2460 /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a36000-70e8a55000 r--s 00000000 fc:00 145 /system/fonts/NotoSansDevanagariUI-Regular.ttf
+70e8a55000-70e8a61000 r-xp 00000000 fc:00 2540 /system/lib64/libstagefright_xmlparser.so
+70e8a61000-70e8a74000 ---p 00000000 00:00 0
+70e8a74000-70e8a75000 r--p 0000f000 fc:00 2540 /system/lib64/libstagefright_xmlparser.so
+70e8a75000-70e8a76000 rw-p 00010000 fc:00 2540 /system/lib64/libstagefright_xmlparser.so
+70e8a76000-70e8a78000 r--s 00000000 fc:00 293 /system/fonts/NotoSansTagbanwa-Regular.ttf
+70e8a78000-70e8a8f000 r--s 00000000 fc:00 161 /system/fonts/NotoSerifKannada-Regular.ttf
+70e8a8f000-70e8b23000 r-xp 00000000 fc:00 2633 /system/lib64/libaudioclient.so
+70e8b23000-70e8b37000 ---p 00000000 00:00 0
+70e8b37000-70e8b49000 r--p 0009e000 fc:00 2633 /system/lib64/libaudioclient.so
+70e8b49000-70e8b55000 rw-p 000b0000 fc:00 2633 /system/lib64/libaudioclient.so
+70e8b55000-70e8b9f000 r--s 00000000 fc:00 83 /system/fonts/RobotoCondensed-Light.ttf
+70e8b9f000-70e8ba1000 r-xp 00000000 fc:00 2520 /system/lib64/libhardware_legacy.so
+70e8ba1000-70e8bbe000 ---p 00000000 00:00 0
+70e8bbe000-70e8bbf000 r--p 0000f000 fc:00 2520 /system/lib64/libhardware_legacy.so
+70e8bbf000-70e8bc0000 rw-p 00010000 fc:00 2520 /system/lib64/libhardware_legacy.so
+70e8bc0000-70e8be0000 r-xp 00000000 fc:00 2410 /system/lib64/android.hidl.memory@1.0.so
+70e8be0000-70e8bfa000 ---p 00000000 00:00 0
+70e8bfa000-70e8bfd000 r--p 0002d000 fc:00 2410 /system/lib64/android.hidl.memory@1.0.so
+70e8bfd000-70e8bfe000 rw-p 00030000 fc:00 2410 /system/lib64/android.hidl.memory@1.0.so
+70e8bfe000-70e8bff000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e8bff000-70e8c02000 r--s 00000000 fc:00 273 /system/fonts/NotoSansSundanese-Regular.ttf
+70e8c02000-70e8c0f000 r--s 00000000 fc:00 115 /system/fonts/NotoSansAdlam-Regular.ttf
+70e8c0f000-70e8c18000 r-xp 00000000 fc:00 2350 /system/lib64/libnetdutils.so
+70e8c18000-70e8c2e000 ---p 00000000 00:00 0
+70e8c2e000-70e8c2f000 r--p 0000f000 fc:00 2350 /system/lib64/libnetdutils.so
+70e8c2f000-70e8c30000 rw-p 00010000 fc:00 2350 /system/lib64/libnetdutils.so
+70e8c30000-70e8c44000 r--s 00000000 fc:00 283 /system/fonts/NotoSansKannadaUI-Regular.ttf
+70e8c44000-70e8c45000 r-xp 00000000 fc:00 2926 /system/lib64/libhidlallocatorutils.so
+70e8c45000-70e8c63000 ---p 00000000 00:00 0
+70e8c63000-70e8c64000 r--p 0000f000 fc:00 2926 /system/lib64/libhidlallocatorutils.so
+70e8c64000-70e8c65000 rw-p 00010000 fc:00 2926 /system/lib64/libhidlallocatorutils.so
+70e8c65000-70e8c67000 r--s 00000000 fc:00 65 /system/fonts/NotoSansTagalog-Regular.ttf
+70e8c67000-70e8c70000 r--s 00000000 fc:00 294 /system/fonts/NotoSansChakma-Regular.ttf
+70e8c70000-70e8c92000 r--s 00000000 fc:00 116 /system/fonts/NotoSansDevanagari-Regular.ttf
+70e8c92000-70e8c94000 r-xp 00000000 fc:00 2501 /system/lib64/libsync.so
+70e8c94000-70e8cb1000 ---p 00000000 00:00 0
+70e8cb1000-70e8cb2000 r--p 0000f000 fc:00 2501 /system/lib64/libsync.so
+70e8cb2000-70e8cb3000 rw-p 00010000 fc:00 2501 /system/lib64/libsync.so
+70e8cb3000-70e8cc6000 r--s 00000000 fc:00 196 /system/fonts/NotoSerifSinhala-Regular.otf
+70e8cc6000-70e8d5c000 r-xp 00000000 fc:00 2403 /system/lib64/libmedia.so
+70e8d5c000-70e8d70000 ---p 00000000 00:00 0
+70e8d70000-70e8d88000 r--p 00098000 fc:00 2403 /system/lib64/libmedia.so
+70e8d88000-70e8d95000 rw-p 000b0000 fc:00 2403 /system/lib64/libmedia.so
+70e8d95000-70e8d96000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70e8d96000-70e8d99000 r--s 00000000 fc:00 247 /system/fonts/NotoSansSamaritan-Regular.ttf
+70e8d99000-70e8dad000 r--s 00000000 fc:00 108 /system/fonts/NotoSansKannada-Bold.ttf
+70e8dad000-70e8dcd000 r--s 00000000 fc:00 303 /system/fonts/NotoSerifEthiopic-Bold.otf
+70e8dcd000-70e8de5000 r-xp 00000000 fc:00 2954 /system/lib64/libGLESv2.so
+70e8de5000-70e8dfc000 ---p 00000000 00:00 0
+70e8dfc000-70e8dfd000 r--p 0001f000 fc:00 2954 /system/lib64/libGLESv2.so
+70e8dfd000-70e8dfe000 rw-p 00020000 fc:00 2954 /system/lib64/libGLESv2.so
+70e8dfe000-70e8e06000 r--s 00000000 fc:00 265 /system/fonts/NotoSansBalinese-Regular.ttf
+70e8e06000-70e8e0e000 r--s 00000000 fc:00 219 /system/fonts/NotoSansLaoUI-Bold.ttf
+70e8e0e000-70e8e12000 r-xp 00000000 fc:00 2617 /system/lib64/libdebuggerd_client.so
+70e8e12000-70e8e2d000 ---p 00000000 00:00 0
+70e8e2d000-70e8e2e000 r--p 0000f000 fc:00 2617 /system/lib64/libdebuggerd_client.so
+70e8e2e000-70e8e2f000 rw-p 00010000 fc:00 2617 /system/lib64/libdebuggerd_client.so
+70e8e2f000-70e8e4b000 r--s 00000000 fc:00 211 /system/fonts/NotoSerifEthiopic-Regular.otf
+70e8e4b000-70e8e5e000 r-xp 00000000 fc:00 2484 /system/lib64/android.hardware.memtrack@1.0.so
+70e8e5e000-70e8e78000 ---p 00000000 00:00 0
+70e8e78000-70e8e7a000 r--p 0001e000 fc:00 2484 /system/lib64/android.hardware.memtrack@1.0.so
+70e8e7a000-70e8e7b000 rw-p 00020000 fc:00 2484 /system/lib64/android.hardware.memtrack@1.0.so
+70e8e7b000-70e8e7d000 r--s 00000000 fc:00 261 /system/fonts/NotoSansShavian-Regular.ttf
+70e8e7d000-70e8e80000 r--s 00000000 fc:00 204 /system/fonts/NotoSansRunic-Regular.ttf
+70e8e80000-70e8ea1000 r-xp 00000000 fc:00 2512 /system/lib64/android.hardware.configstore@1.0.so
+70e8ea1000-70e8ebb000 ---p 00000000 00:00 0
+70e8ebb000-70e8ebe000 r--p 0002d000 fc:00 2512 /system/lib64/android.hardware.configstore@1.0.so
+70e8ebe000-70e8ebf000 rw-p 00030000 fc:00 2512 /system/lib64/android.hardware.configstore@1.0.so
+70e8ebf000-70e8ee3000 r--s 00000000 fc:00 226 /system/fonts/NotoSansEthiopic-Bold.ttf
+70e8ee3000-70e8f1a000 r-xp 00000000 fc:00 2337 /system/lib64/libm.so
+70e8f1a000-70e8f32000 ---p 00000000 00:00 0
+70e8f32000-70e8f33000 r--p 0003f000 fc:00 2337 /system/lib64/libm.so
+70e8f33000-70e8f34000 rw-p 00040000 fc:00 2337 /system/lib64/libm.so
+70e8f34000-70e8f36000 r--s 00000000 fc:00 133 /system/fonts/NotoSansRejang-Regular.ttf
+70e8f36000-70e8f38000 r--s 00000000 fc:00 69 /system/fonts/NotoSansPhoenician-Regular.ttf
+70e8f38000-70e8f40000 r--s 00000000 fc:00 245 /system/fonts/NotoSansLaoUI-Regular.ttf
+70e8f40000-70e8f45000 r-xp 00000000 fc:00 2341 /system/lib64/libstagefright_codecbase.so
+70e8f45000-70e8f5f000 ---p 00000000 00:00 0
+70e8f5f000-70e8f60000 r--p 0000f000 fc:00 2341 /system/lib64/libstagefright_codecbase.so
+70e8f60000-70e8f61000 rw-p 00010000 fc:00 2341 /system/lib64/libstagefright_codecbase.so
+70e8f61000-70e8f62000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70e8f62000-70e8f66000 r--s 00000000 fc:00 225 /system/fonts/NotoSansOldPersian-Regular.ttf
+70e8f66000-70e8f89000 r--s 00000000 fc:00 167 /system/fonts/NotoSansEthiopic-Regular.ttf
+70e8f89000-70e8f92000 r-xp 00000000 fc:00 2515 /system/lib64/libGLESv1_CM.so
+70e8f92000-70e8fa8000 ---p 00000000 00:00 0
+70e8fa8000-70e8fa9000 r--p 0000f000 fc:00 2515 /system/lib64/libGLESv1_CM.so
+70e8fa9000-70e8faa000 rw-p 00010000 fc:00 2515 /system/lib64/libGLESv1_CM.so
+70e8faa000-70e8fad000 r--s 00000000 fc:00 206 /system/fonts/NotoSansOsage-Regular.ttf
+70e8fad000-70e8fb5000 r--s 00000000 fc:00 121 /system/fonts/NotoSerifLao-Bold.ttf
+70e8fb5000-70e8fd3000 r--s 00000000 fc:00 68 /system/fonts/NotoNaskhArabicUI-Bold.ttf
+70e8fd3000-70e9010000 r-xp 00000000 fc:00 2600 /system/lib64/android.hardware.cas@1.0.so
+70e9010000-70e9026000 ---p 00000000 00:00 0
+70e9026000-70e902c000 r--p 0004a000 fc:00 2600 /system/lib64/android.hardware.cas@1.0.so
+70e902c000-70e902d000 rw-p 00050000 fc:00 2600 /system/lib64/android.hardware.cas@1.0.so
+70e902d000-70e902e000 r--s 00000000 00:05 31475 /dev/ashmem/5c7d41a6-003d-45a5-9e3b-2d34c5829a2d (deleted)
+70e902e000-70e9043000 r--s 00000000 fc:00 120 /system/fonts/NotoSansKannada-Regular.ttf
+70e9043000-70e9067000 r-xp 00000000 fc:00 2729 /system/lib64/libexpat.so
+70e9067000-70e9081000 ---p 00000000 00:00 0
+70e9081000-70e9083000 r--p 0002e000 fc:00 2729 /system/lib64/libexpat.so
+70e9083000-70e9084000 rw-p 00030000 fc:00 2729 /system/lib64/libexpat.so
+70e9084000-70e9086000 r--s 00000000 fc:00 155 /system/fonts/NotoSansOsmanya-Regular.ttf
+70e9086000-70e90c3000 r--s 00000000 fc:00 91 /system/fonts/NotoSerif-Regular.ttf
+70e90c3000-70e91ce000 r-xp 00000000 fc:00 2647 /system/lib64/libcrypto.so
+70e91ce000-70e91e2000 ---p 00000000 00:00 0
+70e91e2000-70e91f3000 r--p 0010f000 fc:00 2647 /system/lib64/libcrypto.so
+70e91f3000-70e91f4000 rw-p 00120000 fc:00 2647 /system/lib64/libcrypto.so
+70e91f4000-70e91f5000 rw-p 00000000 00:00 0 [anon:.bss]
+70e91f5000-70e91fa000 r--s 00000000 fc:00 149 /system/fonts/NotoSansNKo-Regular.ttf
+70e91fa000-70e9202000 r--s 00000000 fc:00 198 /system/fonts/NotoSerifLao-Regular.ttf
+70e9202000-70e921b000 r-xp 00000000 fc:00 2682 /system/lib64/libmedia_helper.so
+70e921b000-70e922d000 ---p 00000000 00:00 0
+70e922d000-70e9230000 r--p 0001d000 fc:00 2682 /system/lib64/libmedia_helper.so
+70e9230000-70e9231000 rw-p 00020000 fc:00 2682 /system/lib64/libmedia_helper.so
+70e9231000-70e924a000 r--s 00000000 fc:00 232 /system/fonts/NotoSansBengali-Bold.ttf
+70e924a000-70e9256000 r-xp 00000000 fc:00 2467 /system/lib64/libsoundtrigger.so
+70e9256000-70e9272000 ---p 00000000 00:00 0
+70e9272000-70e9276000 r--p 0000c000 fc:00 2467 /system/lib64/libsoundtrigger.so
+70e9276000-70e9277000 rw-p 00010000 fc:00 2467 /system/lib64/libsoundtrigger.so
+70e9277000-70e9278000 r--s 00000000 fc:01 1177 /vendor/overlay/Pixel/PixelThemeOverlay.apk
+70e9278000-70e927c000 r--s 00000000 fc:00 215 /system/fonts/NotoSansNewTaiLue-Regular.ttf
+70e927c000-70e929a000 r--s 00000000 fc:00 235 /system/fonts/NotoNaskhArabicUI-Regular.ttf
+70e929a000-70e929b000 r-xp 00000000 fc:00 2375 /system/lib64/android.hardware.graphics.common@1.1.so
+70e929b000-70e92b9000 ---p 00000000 00:00 0
+70e92b9000-70e92ba000 r--p 0000f000 fc:00 2375 /system/lib64/android.hardware.graphics.common@1.1.so
+70e92ba000-70e92bb000 rw-p 00010000 fc:00 2375 /system/lib64/android.hardware.graphics.common@1.1.so
+70e92bb000-70e92da000 r--s 00000000 fc:00 281 /system/fonts/GoogleSans-BoldItalic.ttf
+70e92da000-70e9302000 r-xp 00000000 fc:00 2529 /system/lib64/libinput.so
+70e9302000-70e931b000 ---p 00000000 00:00 0
+70e931b000-70e9322000 r--p 00029000 fc:00 2529 /system/lib64/libinput.so
+70e9322000-70e9323000 rw-p 00030000 fc:00 2529 /system/lib64/libinput.so
+70e9323000-70e9340000 r--s 00000000 fc:00 130 /system/fonts/NotoNaskhArabic-Bold.ttf
+70e9340000-70e934b000 r-xp 00000000 fc:00 2451 /system/lib64/libbpf.so
+70e934b000-70e935f000 ---p 00000000 00:00 0
+70e935f000-70e9360000 r--p 0000f000 fc:00 2451 /system/lib64/libbpf.so
+70e9360000-70e9361000 rw-p 00010000 fc:00 2451 /system/lib64/libbpf.so
+70e9361000-70e9367000 r--s 00000000 fc:00 96 /system/fonts/NotoSansKharoshthi-Regular.ttf
+70e9367000-70e9385000 r--s 00000000 fc:00 151 /system/fonts/GoogleSans-Bold.ttf
+70e9385000-70e93b8000 r-xp 00000000 fc:00 2494 /system/lib64/libpng.so
+70e93b8000-70e93d4000 ---p 00000000 00:00 0
+70e93d4000-70e93d5000 r--p 0003f000 fc:00 2494 /system/lib64/libpng.so
+70e93d5000-70e93d6000 rw-p 00040000 fc:00 2494 /system/lib64/libpng.so
+70e93d6000-70e93d7000 r--s 00004000 fc:01 1177 /vendor/overlay/Pixel/PixelThemeOverlay.apk
+70e93d7000-70e93d9000 r--s 00000000 fc:00 295 /system/fonts/NotoSansOldTurkic-Regular.ttf
+70e93d9000-70e93e5000 r--s 00000000 fc:00 86 /system/fonts/NotoSerifKhmer-Bold.otf
+70e93e5000-70e9404000 r--s 00000000 fc:00 240 /system/fonts/GoogleSans-MediumItalic.ttf
+70e9404000-70e9409000 r-xp 00000000 fc:00 2404 /system/lib64/libhidlmemory.so
+70e9409000-70e9423000 ---p 00000000 00:00 0
+70e9423000-70e9424000 r--p 0000f000 fc:00 2404 /system/lib64/libhidlmemory.so
+70e9424000-70e9425000 rw-p 00010000 fc:00 2404 /system/lib64/libhidlmemory.so
+70e9425000-70e943e000 r--s 00000000 fc:00 185 /system/fonts/NotoSansBengali-Regular.ttf
+70e943e000-70e945c000 r--s 00000000 fc:00 129 /system/fonts/GoogleSans-Medium.ttf
+70e945c000-70e960c000 r-xp 00000000 fc:00 2398 /system/lib64/libstagefright.so
+70e960c000-70e9625000 ---p 00000000 00:00 0
+70e9625000-70e9638000 r--p 001bd000 fc:00 2398 /system/lib64/libstagefright.so
+70e9638000-70e966c000 rw-p 001d0000 fc:00 2398 /system/lib64/libstagefright.so
+70e966c000-70e966d000 rw-p 00000000 00:00 0 [anon:.bss]
+70e966d000-70e966e000 r--s 00000000 103:1d 1474566 /data/resource-cache/vendor@overlay@Pixel@PixelThemeOverlay.apk@idmap
+70e966e000-70e9672000 r--s 00000000 fc:00 241 /system/fonts/NotoSansMeeteiMayek-Regular.ttf
+70e9672000-70e9680000 r--s 00000000 fc:00 150 /system/fonts/NotoSansMalayalamUI-Bold.ttf
+70e9680000-70e96a7000 r-xp 00000000 fc:00 2365 /system/lib64/libhwbinder.so
+70e96a7000-70e96bd000 ---p 00000000 00:00 0
+70e96bd000-70e96bf000 r--p 0002e000 fc:00 2365 /system/lib64/libhwbinder.so
+70e96bf000-70e96c0000 rw-p 00030000 fc:00 2365 /system/lib64/libhwbinder.so
+70e96c0000-70e96c1000 r--s 00088000 103:1d 1736830 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk
+70e96c1000-70e96cb000 r--s 00000000 fc:00 94 /system/fonts/NotoSansKhmerUI-Regular.ttf
+70e96cb000-70e96d9000 r--s 00000000 fc:00 275 /system/fonts/NotoSansMalayalamUI-Regular.ttf
+70e96d9000-70e96dd000 r-xp 00000000 fc:00 2386 /system/lib64/libusbhost.so
+70e96dd000-70e96f8000 ---p 00000000 00:00 0
+70e96f8000-70e96f9000 r--p 0000f000 fc:00 2386 /system/lib64/libusbhost.so
+70e96f9000-70e96fa000 rw-p 00010000 fc:00 2386 /system/lib64/libusbhost.so
+70e96fa000-70e96fb000 r--p 00000000 00:05 10266154 [anon:dalvik-classes.dex extracted in memory from /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]
+70e96fb000-70e9701000 r--s 00000000 fc:00 280 /system/fonts/NotoSansCoptic-Regular.ttf
+70e9701000-70e9720000 r-xp 00000000 fc:00 2490 /system/lib64/libstagefright_bufferqueue_helper.so
+70e9720000-70e973b000 ---p 00000000 00:00 0
+70e973b000-70e973e000 r--p 0002d000 fc:00 2490 /system/lib64/libstagefright_bufferqueue_helper.so
+70e973e000-70e9740000 rw-p 00030000 fc:00 2490 /system/lib64/libstagefright_bufferqueue_helper.so
+70e9740000-70e9742000 r--s 00000000 fc:00 141 /system/fonts/NotoSansOldSouthArabian-Regular.ttf
+70e9742000-70e974c000 r--s 00000000 fc:00 229 /system/fonts/NotoSerifKhmer-Regular.otf
+70e974c000-70e9759000 r--s 00000000 fc:00 260 /system/fonts/NotoSerifMalayalam-Bold.ttf
+70e9759000-70e97c7000 r-xp 00000000 fc:00 2428 /system/lib64/libstagefright_omx.so
+70e97c7000-70e97dc000 ---p 00000000 00:00 0
+70e97dc000-70e97e6000 r--p 00076000 fc:00 2428 /system/lib64/libstagefright_omx.so
+70e97e6000-70e97ed000 rw-p 00080000 fc:00 2428 /system/lib64/libstagefright_omx.so
+70e97ed000-70e97fa000 r--s 00000000 fc:00 66 /system/fonts/NotoSerifMalayalam-Regular.ttf
+70e97fa000-70e9819000 r--s 00000000 fc:00 183 /system/fonts/GoogleSans-Italic.ttf
+70e9819000-70e9856000 r-xp 00000000 fc:00 2434 /system/lib64/libprotobuf-cpp-lite.so
+70e9856000-70e9867000 ---p 00000000 00:00 0
+70e9867000-70e9869000 r--p 0003e000 fc:00 2434 /system/lib64/libprotobuf-cpp-lite.so
+70e9869000-70e986a000 rw-p 00040000 fc:00 2434 /system/lib64/libprotobuf-cpp-lite.so
+70e986a000-70e9873000 r--s 00000000 fc:00 134 /system/fonts/NotoSansKhmerUI-Bold.ttf
+70e9873000-70e9891000 r--s 00000000 fc:00 81 /system/fonts/GoogleSans-Regular.ttf
+70e9891000-70e989e000 r-xp 00000000 fc:00 2377 /system/lib64/libmediametrics.so
+70e989e000-70e98ae000 ---p 00000000 00:00 0
+70e98ae000-70e98b0000 r--p 0000e000 fc:00 2377 /system/lib64/libmediametrics.so
+70e98b0000-70e98b1000 rw-p 00010000 fc:00 2377 /system/lib64/libmediametrics.so
+70e98b1000-70e98b9000 r--s 00000000 fc:00 152 /system/fonts/NotoSansLao-Bold.ttf
+70e98b9000-70e98c7000 r--s 00000000 fc:00 279 /system/fonts/NotoSansMalayalam-Bold.ttf
+70e98c7000-70e98ca000 r-xp 00000000 fc:00 2952 /system/lib64/libETC1.so
+70e98ca000-70e98e6000 ---p 00000000 00:00 0
+70e98e6000-70e98e7000 r--p 0000f000 fc:00 2952 /system/lib64/libETC1.so
+70e98e7000-70e98e8000 rw-p 00010000 fc:00 2952 /system/lib64/libETC1.so
+70e98e8000-70e98e9000 r--s 00000000 fc:00 1121 /system/usr/hyphen-data/hyph-und-ethi.hyb
+70e98e9000-70e98ef000 r--s 00000000 fc:00 64 /system/fonts/NotoSansBrahmi-Regular.ttf
+70e98ef000-70e990f000 rw-p 00000000 00:05 10271012 [anon:dalvik-CompilerMetadata]
+70e990f000-70e9926000 r-xp 00000000 fc:00 2526 /system/lib64/libbacktrace.so
+70e9926000-70e993e000 ---p 00000000 00:00 0
+70e993e000-70e993f000 r--p 0001f000 fc:00 2526 /system/lib64/libbacktrace.so
+70e993f000-70e9940000 rw-p 00020000 fc:00 2526 /system/lib64/libbacktrace.so
+70e9940000-70e9941000 r--s 00000000 fc:00 1129 /system/usr/hyphen-data/hyph-tk.hyb
+70e9941000-70e99a4000 r-xp 00000000 fc:00 2528 /system/lib64/libcamera_client.so
+70e99a4000-70e99bc000 ---p 00000000 00:00 0
+70e99bc000-70e99c9000 r--p 00063000 fc:00 2528 /system/lib64/libcamera_client.so
+70e99c9000-70e99d0000 rw-p 00070000 fc:00 2528 /system/lib64/libcamera_client.so
+70e99d0000-70e99d1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e99d1000-70e99d3000 r--s 00000000 fc:00 200 /system/fonts/NotoSansOldItalic-Regular.ttf
+70e99d3000-70e99e1000 r--s 00000000 fc:00 153 /system/fonts/NotoSansMalayalam-Regular.ttf
+70e99e1000-70e9a01000 rw-p 00000000 00:05 10271011 [anon:dalvik-CompilerMetadata]
+70e9a01000-70e9a1e000 r-xp 00000000 fc:00 2542 /system/lib64/libimg_utils.so
+70e9a1e000-70e9a39000 ---p 00000000 00:00 0
+70e9a39000-70e9a3c000 r--p 0001d000 fc:00 2542 /system/lib64/libimg_utils.so
+70e9a3c000-70e9a3f000 rw-p 00020000 fc:00 2542 /system/lib64/libimg_utils.so
+70e9a3f000-70e9a5c000 r--s 00000000 fc:00 262 /system/fonts/NotoNaskhArabic-Regular.ttf
+70e9a5c000-70e9a69000 r-xp 00000000 fc:00 2706 /system/lib64/libziparchive.so
+70e9a69000-70e9a7b000 ---p 00000000 00:00 0
+70e9a7b000-70e9a7c000 r--p 0000f000 fc:00 2706 /system/lib64/libziparchive.so
+70e9a7c000-70e9a7d000 rw-p 00010000 fc:00 2706 /system/lib64/libziparchive.so
+70e9a7d000-70e9a85000 r--s 00000000 fc:00 119 /system/fonts/NotoSansLao-Regular.ttf
+70e9a85000-70e9a8e000 r--s 00000000 fc:00 77 /system/fonts/NotoSansTamilUI-Bold.ttf
+70e9a8e000-70e9a97000 r--s 00000000 fc:00 160 /system/fonts/NotoSansTamilUI-Regular.ttf
+70e9a97000-70e9a9d000 r-xp 00000000 fc:00 2536 /system/lib64/libnativehelper.so
+70e9a9d000-70e9ab6000 ---p 00000000 00:00 0
+70e9ab6000-70e9ab7000 r--p 0000f000 fc:00 2536 /system/lib64/libnativehelper.so
+70e9ab7000-70e9ab8000 rw-p 00010000 fc:00 2536 /system/lib64/libnativehelper.so
+70e9ab8000-70e9ab9000 r--s 00000000 fc:00 1134 /system/usr/hyphen-data/hyph-te.hyb
+70e9ab9000-70e9ac2000 r--s 00000000 fc:00 246 /system/fonts/NotoSerifTamil-Bold.ttf
+70e9ac2000-70e9acb000 r--s 00000000 fc:00 302 /system/fonts/NotoSerifTamil-Regular.ttf
+70e9acb000-70e9af4000 r-xp 00000000 fc:00 2950 /system/lib64/libmemunreachable.so
+70e9af4000-70e9b09000 ---p 00000000 00:00 0
+70e9b09000-70e9b0b000 r--p 0002e000 fc:00 2950 /system/lib64/libmemunreachable.so
+70e9b0b000-70e9b0c000 rw-p 00030000 fc:00 2950 /system/lib64/libmemunreachable.so
+70e9b0c000-70e9b0d000 r--s 00000000 fc:00 1088 /system/usr/hyphen-data/hyph-ta.hyb
+70e9b0d000-70e9b0f000 r--s 00000000 fc:00 72 /system/fonts/NotoSansOlChiki-Regular.ttf
+70e9b0f000-70e9b2f000 rw-p 00000000 00:05 10271010 [anon:dalvik-CompilerMetadata]
+70e9b2f000-70e9b4f000 r--s 00000000 00:10 16633 /dev/__properties__/u:object_r:persist_debug_prop:s0
+70e9b4f000-70e9b65000 r-xp 00000000 fc:00 2920 /system/lib64/android.hardware.cas.native@1.0.so
+70e9b65000-70e9b7b000 ---p 00000000 00:00 0
+70e9b7b000-70e9b7d000 r--p 0001e000 fc:00 2920 /system/lib64/android.hardware.cas.native@1.0.so
+70e9b7d000-70e9b7e000 rw-p 00020000 fc:00 2920 /system/lib64/android.hardware.cas.native@1.0.so
+70e9b7e000-70e9b7f000 r--s 00000000 fc:00 1145 /system/usr/hyphen-data/hyph-pt.hyb
+70e9b7f000-70e9b83000 r--s 00000000 fc:00 277 /system/fonts/NotoSansMandaic-Regular.ttf
+70e9b83000-70e9bdb000 r-xp 00000000 fc:00 2334 /system/lib64/libsonivox.so
+70e9bdb000-70e9bf2000 ---p 00000000 00:00 0
+70e9bf2000-70e9bf3000 r--p 0005f000 fc:00 2334 /system/lib64/libsonivox.so
+70e9bf3000-70e9bf4000 rw-p 00060000 fc:00 2334 /system/lib64/libsonivox.so
+70e9bf4000-70e9bfb000 rw-p 00000000 00:00 0 [anon:.bss]
+70e9bfb000-70e9c01000 r--s 00000000 fc:00 123 /system/fonts/NotoSansCham-Bold.ttf
+70e9c01000-70e9c0a000 r--s 00000000 fc:00 255 /system/fonts/NotoSansTamil-Bold.ttf
+70e9c0a000-70e9c13000 r--s 00000000 fc:00 135 /system/fonts/NotoSansTamil-Regular.ttf
+70e9c13000-70e9c15000 r-xp 00000000 fc:00 2519 /system/lib64/libgraphicsenv.so
+70e9c15000-70e9c32000 ---p 00000000 00:00 0
+70e9c32000-70e9c33000 r--p 0000f000 fc:00 2519 /system/lib64/libgraphicsenv.so
+70e9c33000-70e9c34000 rw-p 00010000 fc:00 2519 /system/lib64/libgraphicsenv.so
+70e9c34000-70e9c3a000 r--s 00000000 fc:00 259 /system/fonts/NotoSansCham-Regular.ttf
+70e9c3a000-70e9c42000 r--s 00000000 fc:00 114 /system/fonts/NotoSansGurmukhiUI-Bold.ttf
+70e9c42000-70e9c4a000 r--s 00000000 fc:00 122 /system/fonts/NotoSansGurmukhiUI-Regular.ttf
+70e9c4a000-70e9c5f000 r-xp 00000000 fc:00 2348 /system/lib64/android.hidl.allocator@1.0.so
+70e9c5f000-70e9c77000 ---p 00000000 00:00 0
+70e9c77000-70e9c79000 r--p 0001e000 fc:00 2348 /system/lib64/android.hidl.allocator@1.0.so
+70e9c79000-70e9c7a000 rw-p 00020000 fc:00 2348 /system/lib64/android.hidl.allocator@1.0.so
+70e9c7a000-70e9c7b000 r--s 00000000 fc:00 1095 /system/usr/hyphen-data/hyph-pa.hyb
+70e9c7b000-70e9c83000 r--s 00000000 fc:00 298 /system/fonts/NotoSerifGurmukhi-Bold.otf
+70e9c83000-70e9d01000 r-xp 00000000 fc:00 2665 /system/lib64/libbinder.so
+70e9d01000-70e9d1e000 ---p 00000000 00:00 0
+70e9d1e000-70e9d2e000 r--p 00080000 fc:00 2665 /system/lib64/libbinder.so
+70e9d2e000-70e9d2f000 rw-p 00090000 fc:00 2665 /system/lib64/libbinder.so
+70e9d2f000-70e9d4f000 rw-p 00000000 00:05 10271009 [anon:dalvik-CompilerMetadata]
+70e9d4f000-70e9d53000 r-xp 00000000 fc:00 2454 /system/lib64/libaudiomanager.so
+70e9d53000-70e9d6e000 ---p 00000000 00:00 0
+70e9d6e000-70e9d6f000 r--p 0000f000 fc:00 2454 /system/lib64/libaudiomanager.so
+70e9d6f000-70e9d70000 rw-p 00010000 fc:00 2454 /system/lib64/libaudiomanager.so
+70e9d70000-70e9d71000 r--s 00000000 fc:00 1087 /system/usr/hyphen-data/hyph-or.hyb
+70e9d71000-70e9d91000 rw-p 00000000 00:05 10271008 [anon:dalvik-CompilerMetadata]
+70e9d91000-70e9e21000 r-xp 00000000 fc:00 2627 /system/lib64/libft2.so
+70e9e21000-70e9e37000 ---p 00000000 00:00 0
+70e9e37000-70e9e3c000 r--p 0009b000 fc:00 2627 /system/lib64/libft2.so
+70e9e3c000-70e9e3d000 rw-p 000a0000 fc:00 2627 /system/lib64/libft2.so
+70e9e3d000-70e9e3e000 r--s 00000000 fc:00 1142 /system/usr/hyphen-data/hyph-mr.hyb
+70e9e3e000-70e9e45000 r--s 00000000 fc:00 88 /system/fonts/NotoSerifGurmukhi-Regular.otf
+70e9e45000-70e9e65000 r--s 00000000 00:10 16594 /dev/__properties__/u:object_r:exported3_default_prop:s0
+70e9e65000-70e9e7f000 r-xp 00000000 fc:00 2643 /system/lib64/libunwind.so
+70e9e7f000-70e9e94000 ---p 00000000 00:00 0
+70e9e94000-70e9e95000 r--p 0001f000 fc:00 2643 /system/lib64/libunwind.so
+70e9e95000-70e9e96000 rw-p 00020000 fc:00 2643 /system/lib64/libunwind.so
+70e9e96000-70e9eff000 rw-p 00000000 00:00 0 [anon:.bss]
+70e9eff000-70e9f00000 r--s 00000000 fc:00 1130 /system/usr/hyphen-data/hyph-ml.hyb
+70e9f00000-70e9f02000 r--s 00000000 fc:00 75 /system/fonts/NotoSansOgham-Regular.ttf
+70e9f02000-70e9f0a000 r--s 00000000 fc:00 193 /system/fonts/NotoSansGurmukhi-Bold.ttf
+70e9f0a000-70ea022000 r-xp 00000000 fc:00 2328 /system/lib64/libsqlite.so
+70ea022000-70ea033000 ---p 00000000 00:00 0
+70ea033000-70ea036000 r--p 0011d000 fc:00 2328 /system/lib64/libsqlite.so
+70ea036000-70ea038000 rw-p 00120000 fc:00 2328 /system/lib64/libsqlite.so
+70ea038000-70ea03c000 r--s 00000000 fc:00 285 /system/fonts/NotoSansGlagolitic-Regular.ttf
+70ea03c000-70ea04c000 r--s 00000000 fc:00 184 /system/fonts/NotoSerifGujarati-Bold.ttf
+70ea04c000-70ea060000 r-xp 00000000 fc:00 2731 /system/lib64/libstatslog.so
+70ea060000-70ea07b000 ---p 00000000 00:00 0
+70ea07b000-70ea07c000 r--p 0001f000 fc:00 2731 /system/lib64/libstatslog.so
+70ea07c000-70ea07d000 rw-p 00020000 fc:00 2731 /system/lib64/libstatslog.so
+70ea07d000-70ea081000 r--s 00000000 fc:00 182 /system/fonts/NotoSansBatak-Regular.ttf
+70ea081000-70ea091000 r--s 00000000 fc:00 264 /system/fonts/NotoSerifGujarati-Regular.ttf
+70ea091000-70ea160000 r-xp 00000000 fc:00 2728 /system/lib64/libdng_sdk.so
+70ea160000-70ea173000 ---p 00000000 00:00 0
+70ea173000-70ea17a000 r--p 000d9000 fc:00 2728 /system/lib64/libdng_sdk.so
+70ea17a000-70ea17b000 rw-p 000e0000 fc:00 2728 /system/lib64/libdng_sdk.so
+70ea17b000-70ea198000 r--s 00000000 fc:00 223 /system/fonts/DancingScript-Bold.ttf
+70ea198000-70ea19c000 r-xp 00000000 fc:00 2344 /system/lib64/libnativewindow.so
+70ea19c000-70ea1b7000 ---p 00000000 00:00 0
+70ea1b7000-70ea1b8000 r--p 0000f000 fc:00 2344 /system/lib64/libnativewindow.so
+70ea1b8000-70ea1b9000 rw-p 00010000 fc:00 2344 /system/lib64/libnativewindow.so
+70ea1b9000-70ea1bd000 r--s 00000000 fc:00 98 /system/fonts/NotoSansAhom-Regular.otf
+70ea1bd000-70ea1d1000 r--s 00000000 fc:00 104 /system/fonts/NotoSerifDevanagari-Bold.ttf
+70ea1d1000-70ea1d4000 r-xp 00000000 fc:00 2923 /system/lib64/libstdc++.so
+70ea1d4000-70ea1f0000 ---p 00000000 00:00 0
+70ea1f0000-70ea1f1000 r--p 0000f000 fc:00 2923 /system/lib64/libstdc++.so
+70ea1f1000-70ea1f2000 rw-p 00010000 fc:00 2923 /system/lib64/libstdc++.so
+70ea1f2000-70ea1f4000 r--s 00000000 fc:00 117 /system/fonts/NotoSansLydian-Regular.ttf
+70ea1f4000-70ea211000 r--s 00000000 fc:00 239 /system/fonts/DancingScript-Regular.ttf
+70ea211000-70ea24a000 r-xp 00000000 fc:00 2933 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea24a000-70ea268000 ---p 00000000 00:00 0
+70ea268000-70ea26c000 r--p 0003c000 fc:00 2933 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea26c000-70ea26d000 rw-p 00040000 fc:00 2933 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea26d000-70ea26f000 r--s 00000000 fc:00 244 /system/fonts/NotoSansLycian-Regular.ttf
+70ea26f000-70ea273000 r--s 00000000 fc:00 173 /system/fonts/NotoSansThaana-Bold.ttf
+70ea273000-70ea287000 r--s 00000000 fc:00 106 /system/fonts/NotoSerifDevanagari-Regular.ttf
+70ea287000-70ea29e000 r-xp 00000000 fc:00 2938 /system/lib64/libpiex.so
+70ea29e000-70ea2b6000 ---p 00000000 00:00 0
+70ea2b6000-70ea2b7000 r--p 0001f000 fc:00 2938 /system/lib64/libpiex.so
+70ea2b7000-70ea2b8000 rw-p 00020000 fc:00 2938 /system/lib64/libpiex.so
+70ea2b8000-70ea2c0000 r--s 00000000 fc:00 113 /system/fonts/NotoSansGurmukhi-Regular.ttf
+70ea2c0000-70ea2c6000 r--s 00000000 fc:00 263 /system/fonts/NotoSerifGeorgian-Bold.ttf
+70ea2c6000-70ea2cc000 r--s 00000000 fc:00 249 /system/fonts/NotoSerifGeorgian-Regular.ttf
+70ea2cc000-70ea9b4000 r-xp 00000000 fc:00 2532 /system/lib64/libhwui.so
+70ea9b4000-70ea9d3000 ---p 00000000 00:00 0
+70ea9d3000-70eaa0b000 r--p 006e8000 fc:00 2532 /system/lib64/libhwui.so
+70eaa0b000-70eaa0c000 rw-p 00720000 fc:00 2532 /system/lib64/libhwui.so
+70eaa0c000-70eaa11000 rw-p 00000000 00:00 0 [anon:.bss]
+70eaa11000-70eaa12000 r--s 00000000 fc:00 1110 /system/usr/hyphen-data/hyph-la.hyb
+70eaa12000-70eaa16000 r--s 00000000 fc:00 87 /system/fonts/NotoSansThaana-Regular.ttf
+70eaa16000-70eaa1b000 r--s 00000000 fc:00 218 /system/fonts/NotoSansGeorgian-Bold.ttf
+70eaa1b000-70eaa20000 r--s 00000000 fc:00 125 /system/fonts/NotoSansGeorgian-Regular.ttf
+70eaa20000-70eaa40000 rw-p 00000000 00:05 10271007 [anon:dalvik-CompilerMetadata]
+70eaa40000-70eaaa0000 r-xp 00000000 fc:00 2384 /system/lib64/libhidltransport.so
+70eaaa0000-70eaabe000 ---p 00000000 00:00 0
+70eaabe000-70eaac6000 r--p 00068000 fc:00 2384 /system/lib64/libhidltransport.so
+70eaac6000-70eaac7000 rw-p 00070000 fc:00 2384 /system/lib64/libhidltransport.so
+70eaac7000-70eaacb000 r--s 00000000 fc:00 192 /system/fonts/NotoSerifArmenian-Bold.ttf
+70eaacb000-70eaad0000 r--s 00000000 fc:00 210 /system/fonts/NotoSansThaiUI-Bold.ttf
+70eaad0000-70eaaf0000 rw-p 00000000 00:05 10271006 [anon:dalvik-CompilerMetadata]
+70eaaf0000-70eab10000 rw-p 00000000 00:05 10271005 [anon:dalvik-CompilerMetadata]
+70eab10000-70eab57000 r-xp 00000000 fc:00 2546 /system/lib64/libmedia_omx.so
+70eab57000-70eab6d000 ---p 00000000 00:00 0
+70eab6d000-70eab7a000 r--p 00053000 fc:00 2546 /system/lib64/libmedia_omx.so
+70eab7a000-70eab7f000 rw-p 00060000 fc:00 2546 /system/lib64/libmedia_omx.so
+70eab7f000-70eab80000 r--s 00000000 fc:00 1119 /system/usr/hyphen-data/hyph-kn.hyb
+70eab80000-70eab86000 r--s 00000000 fc:00 224 /system/fonts/NotoSansThaiUI-Regular.ttf
+70eab86000-70eab8b000 r--s 00000000 fc:00 300 /system/fonts/NotoSerifThai-Bold.ttf
+70eab8b000-70eabab000 rw-p 00000000 00:05 10271004 [anon:dalvik-CompilerMetadata]
+70eabab000-70eac21000 r-xp 00000000 fc:00 2385 /system/lib64/libvintf.so
+70eac21000-70eac31000 ---p 00000000 00:00 0
+70eac31000-70eac36000 r--p 0007b000 fc:00 2385 /system/lib64/libvintf.so
+70eac36000-70eac37000 rw-p 00080000 fc:00 2385 /system/lib64/libvintf.so
+70eac37000-70eac39000 rw-p 00000000 00:00 0 [anon:.bss]
+70eac39000-70eac3a000 r--s 00000000 fc:00 1104 /system/usr/hyphen-data/hyph-hy.hyb
+70eac3a000-70eac3f000 r--s 00000000 fc:00 212 /system/fonts/NotoSerifThai-Regular.ttf
+70eac3f000-70eac44000 r--s 00000000 fc:00 220 /system/fonts/NotoSansThai-Bold.ttf
+70eac44000-70eacb2000 r-xp 00000000 fc:00 2606 /system/lib64/android.hardware.media.omx@1.0.so
+70eacb2000-70eaccf000 ---p 00000000 00:00 0
+70eaccf000-70eacd8000 r--p 00077000 fc:00 2606 /system/lib64/android.hardware.media.omx@1.0.so
+70eacd8000-70eacd9000 rw-p 00080000 fc:00 2606 /system/lib64/android.hardware.media.omx@1.0.so
+70eacd9000-70eacdf000 r--s 00000000 fc:00 169 /system/fonts/NotoSansThai-Regular.ttf
+70eacdf000-70eace9000 r--s 00000000 fc:00 140 /system/fonts/CarroisGothicSC-Regular.ttf
+70eace9000-70ead09000 rw-p 00000000 00:05 10271003 [anon:dalvik-CompilerMetadata]
+70ead09000-70ead22000 r-xp 00000000 fc:00 2539 /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead22000-70ead34000 ---p 00000000 00:00 0
+70ead34000-70ead37000 r--p 0001d000 fc:00 2539 /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead37000-70ead38000 rw-p 00020000 fc:00 2539 /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead38000-70ead47000 r--s 00000000 fc:00 188 /system/fonts/ComingSoon.ttf
+70ead47000-70ead5d000 r-xp 00000000 fc:00 2379 /system/lib64/libselinux.so
+70ead5d000-70ead76000 ---p 00000000 00:00 0
+70ead76000-70ead77000 r--p 0001f000 fc:00 2379 /system/lib64/libselinux.so
+70ead77000-70ead78000 rw-p 00020000 fc:00 2379 /system/lib64/libselinux.so
+70ead78000-70ead79000 rw-p 00000000 00:00 0 [anon:.bss]
+70ead79000-70ead7d000 r--s 00000000 fc:00 282 /system/fonts/NotoSerifArmenian-Regular.ttf
+70ead7d000-70ead82000 r--s 00000000 fc:00 288 /system/fonts/NotoSerifHebrew-Bold.ttf
+70ead82000-70ead83000 r-xp 00000000 fc:00 2680 /system/lib64/android.hardware.media@1.0.so
+70ead83000-70eada1000 ---p 00000000 00:00 0
+70eada1000-70eada2000 r--p 0000f000 fc:00 2680 /system/lib64/android.hardware.media@1.0.so
+70eada2000-70eada3000 rw-p 00010000 fc:00 2680 /system/lib64/android.hardware.media@1.0.so
+70eada3000-70eada8000 r--s 00000000 fc:00 248 /system/fonts/NotoSerifHebrew-Regular.ttf
+70eada8000-70eadb9000 r--s 00000000 fc:00 252 /system/fonts/CutiveMono.ttf
+70eadb9000-70eadd9000 r--s 00000000 00:10 16641 /dev/__properties__/u:object_r:radio_prop:s0
+70eadd9000-70eadda000 r-xp 00000000 fc:00 2533 /system/lib64/android.hardware.graphics.common@1.0.so
+70eadda000-70eadf8000 ---p 00000000 00:00 0
+70eadf8000-70eadf9000 r--p 0000f000 fc:00 2533 /system/lib64/android.hardware.graphics.common@1.0.so
+70eadf9000-70eadfa000 rw-p 00010000 fc:00 2533 /system/lib64/android.hardware.graphics.common@1.0.so
+70eadfa000-70eadfb000 r--s 00000000 fc:00 1126 /system/usr/hyphen-data/hyph-hr.hyb
+70eadfb000-70eadfd000 r--s 00000000 fc:00 194 /system/fonts/NotoSansLisu-Regular.ttf
+70eadfd000-70eae18000 r--s 00000000 fc:00 201 /system/fonts/DroidSansMono.ttf
+70eae18000-70eae3b000 r-xp 00000000 fc:00 2925 /system/lib64/liblzma.so
+70eae3b000-70eae57000 ---p 00000000 00:00 0
+70eae57000-70eae58000 r--p 0002f000 fc:00 2925 /system/lib64/liblzma.so
+70eae58000-70eae59000 rw-p 00030000 fc:00 2925 /system/lib64/liblzma.so
+70eae59000-70eae5f000 rw-p 00000000 00:00 0 [anon:.bss]
+70eae5f000-70eae62000 r--s 00000000 fc:00 103 /system/fonts/NotoSansLimbu-Regular.ttf
+70eae62000-70eae67000 r--s 00000000 fc:00 236 /system/fonts/NotoSansHebrew-Bold.ttf
+70eae67000-70eae84000 r--s 001c2000 fc:00 990 /system/framework/ext.jar
+70eae84000-70eaea4000 rw-p 00000000 00:05 10269720 [anon:dalvik-LinearAlloc]
+70eaea4000-70eaede000 r-xp 00000000 fc:00 2924 /system/lib64/libwilhelm.so
+70eaede000-70eaefa000 ---p 00000000 00:00 0
+70eaefa000-70eaeff000 r--p 0003b000 fc:00 2924 /system/lib64/libwilhelm.so
+70eaeff000-70eaf00000 rw-p 00040000 fc:00 2924 /system/lib64/libwilhelm.so
+70eaf00000-70eaf03000 r--s 00000000 fc:00 242 /system/fonts/NotoSansElbasan-Regular.otf
+70eaf03000-70eaf21000 r-xp 00000000 fc:00 2415 /system/lib64/libdrmframework.so
+70eaf21000-70eaf38000 ---p 00000000 00:00 0
+70eaf38000-70eaf3d000 r--p 0002b000 fc:00 2415 /system/lib64/libdrmframework.so
+70eaf3d000-70eaf3e000 rw-p 00030000 fc:00 2415 /system/lib64/libdrmframework.so
+70eaf3e000-70eaf43000 r--s 00000000 fc:00 70 /system/fonts/NotoSansHebrew-Regular.ttf
+70eaf43000-70eaf44000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eaf44000-70eaf48000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eaf48000-70eaf49000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eaf49000-70eaf4c000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eaf4c000-70eaf4d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eaf4d000-70eaf4e000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eaf4e000-70eaf52000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eaf52000-70eaf98000 r-xp 00000000 fc:00 2426 /system/lib64/libunwindstack.so
+70eaf98000-70eafb6000 ---p 00000000 00:00 0
+70eafb6000-70eafbd000 r--p 00049000 fc:00 2426 /system/lib64/libunwindstack.so
+70eafbd000-70eafbe000 rw-p 00050000 fc:00 2426 /system/lib64/libunwindstack.so
+70eafbe000-70eafc0000 r--s 00000000 fc:00 162 /system/fonts/NotoSansKayahLi-Regular.ttf
+70eafc0000-70eafe4000 r-xp 00000000 fc:00 2944 /system/lib64/libvulkan.so
+70eafe4000-70eaffc000 ---p 00000000 00:00 0
+70eaffc000-70eaffe000 r--p 0002e000 fc:00 2944 /system/lib64/libvulkan.so
+70eaffe000-70eafff000 rw-p 00030000 fc:00 2944 /system/lib64/libvulkan.so
+70eafff000-70eb001000 r--s 00000000 fc:00 180 /system/fonts/NotoSansInscriptionalParthian-Regular.ttf
+70eb001000-70eb01d000 r-xp 00000000 fc:00 2400 /system/lib64/libbufferhubqueue.so
+70eb01d000-70eb030000 ---p 00000000 00:00 0
+70eb030000-70eb031000 r--p 0001f000 fc:00 2400 /system/lib64/libbufferhubqueue.so
+70eb031000-70eb032000 rw-p 00020000 fc:00 2400 /system/lib64/libbufferhubqueue.so
+70eb032000-70eb036000 r--s 00000000 fc:00 269 /system/fonts/NotoSansArmenian-Bold.ttf
+70eb036000-70eb037000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb037000-70eb03a000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb03a000-70eb03b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb03b000-70eb03c000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb03c000-70eb040000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb040000-70eb042000 r-xp 00000000 fc:00 2935 /system/lib64/libhardware.so
+70eb042000-70eb05f000 ---p 00000000 00:00 0
+70eb05f000-70eb060000 r--p 0000f000 fc:00 2935 /system/lib64/libhardware.so
+70eb060000-70eb061000 rw-p 00010000 fc:00 2935 /system/lib64/libhardware.so
+70eb061000-70eb063000 r--s 00000000 fc:00 171 /system/fonts/NotoSansInscriptionalPahlavi-Regular.ttf
+70eb063000-70eb064000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb064000-70eb067000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb067000-70eb068000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb068000-70eb069000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb069000-70eb06d000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb06d000-70eb06e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb06e000-70eb071000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb071000-70eb072000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb072000-70eb073000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb073000-70eb077000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb077000-70eb078000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb078000-70eb07b000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb07b000-70eb07c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb07c000-70eb07d000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb07d000-70eb081000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb081000-70eb082000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb082000-70eb085000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb085000-70eb086000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb086000-70eb09d000 r-xp 00000000 fc:00 2604 /system/lib64/libz.so
+70eb09d000-70eb0b5000 ---p 00000000 00:00 0
+70eb0b5000-70eb0b6000 r--p 0001f000 fc:00 2604 /system/lib64/libz.so
+70eb0b6000-70eb0b7000 rw-p 00020000 fc:00 2604 /system/lib64/libz.so
+70eb0b7000-70eb0bb000 r--s 00000000 fc:00 289 /system/fonts/NotoSansArmenian-Regular.ttf
+70eb0bb000-70eb0bc000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb0bc000-70eb0c0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb0c0000-70eb0c1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0c1000-70eb0c4000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb0c4000-70eb0c5000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0c5000-70eb0c6000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb0c6000-70eb0ca000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb0ca000-70eb0cb000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0cb000-70eb0ce000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb0ce000-70eb0cf000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0cf000-70eb0ef000 rw-p 00000000 00:05 10270988 [anon:dalvik-LinearAlloc]
+70eb0ef000-70eb5bb000 r-xp 00000000 fc:00 2374 /system/lib64/libpdfium.so
+70eb5bb000-70eb5cf000 ---p 00000000 00:00 0
+70eb5cf000-70eb5e6000 r--p 004d9000 fc:00 2374 /system/lib64/libpdfium.so
+70eb5e6000-70eb5ea000 rw-p 004f0000 fc:00 2374 /system/lib64/libpdfium.so
+70eb5ea000-70eb5f1000 rw-p 00000000 00:00 0 [anon:.bss]
+70eb5f1000-70eb5f2000 r--s 00000000 fc:00 1094 /system/usr/hyphen-data/hyph-hi.hyb
+70eb5f2000-70eb5f6000 rw-p 00000000 00:05 10270982 [anon:dalvik-thread local mark stack]
+70eb5f6000-70eb5fa000 rw-p 00000000 00:05 10270981 [anon:dalvik-thread local mark stack]
+70eb5fa000-70eb5fe000 rw-p 00000000 00:05 10270980 [anon:dalvik-thread local mark stack]
+70eb5fe000-70eb602000 rw-p 00000000 00:05 10270979 [anon:dalvik-thread local mark stack]
+70eb602000-70eb606000 rw-p 00000000 00:05 10270978 [anon:dalvik-thread local mark stack]
+70eb606000-70eb60a000 rw-p 00000000 00:05 10270977 [anon:dalvik-thread local mark stack]
+70eb60a000-70eb60e000 rw-p 00000000 00:05 10270976 [anon:dalvik-thread local mark stack]
+70eb60e000-70eb612000 rw-p 00000000 00:05 10270975 [anon:dalvik-thread local mark stack]
+70eb612000-70eb616000 rw-p 00000000 00:05 10270974 [anon:dalvik-thread local mark stack]
+70eb616000-70eb61a000 r-xp 00000000 fc:00 2479 /system/lib64/libspeexresampler.so
+70eb61a000-70eb635000 ---p 00000000 00:00 0
+70eb635000-70eb636000 r--p 0000f000 fc:00 2479 /system/lib64/libspeexresampler.so
+70eb636000-70eb637000 rw-p 00010000 fc:00 2479 /system/lib64/libspeexresampler.so
+70eb637000-70eb639000 r--s 00000000 fc:00 299 /system/fonts/NotoSansImperialAramaic-Regular.ttf
+70eb639000-70eb63d000 rw-p 00000000 00:05 10270973 [anon:dalvik-thread local mark stack]
+70eb63d000-70eb641000 rw-p 00000000 00:05 10270972 [anon:dalvik-thread local mark stack]
+70eb641000-70eb645000 rw-p 00000000 00:05 10270971 [anon:dalvik-thread local mark stack]
+70eb645000-70eb649000 rw-p 00000000 00:05 10270970 [anon:dalvik-thread local mark stack]
+70eb649000-70eb64d000 rw-p 00000000 00:05 10270969 [anon:dalvik-thread local mark stack]
+70eb64d000-70eb651000 rw-p 00000000 00:05 10270968 [anon:dalvik-thread local mark stack]
+70eb651000-70eb655000 rw-p 00000000 00:05 10270967 [anon:dalvik-thread local mark stack]
+70eb655000-70eb659000 rw-p 00000000 00:05 10270966 [anon:dalvik-thread local mark stack]
+70eb659000-70eb65d000 rw-p 00000000 00:05 10270965 [anon:dalvik-thread local mark stack]
+70eb65d000-70eb661000 rw-p 00000000 00:05 10270964 [anon:dalvik-thread local mark stack]
+70eb661000-70eb6c5000 r-xp 00000000 fc:00 2461 /system/lib64/libhidl-gen-utils.so
+70eb6c5000-70eb6df000 ---p 00000000 00:00 0
+70eb6df000-70eb6e1000 r--p 0006e000 fc:00 2461 /system/lib64/libhidl-gen-utils.so
+70eb6e1000-70eb6e2000 rw-p 00070000 fc:00 2461 /system/lib64/libhidl-gen-utils.so
+70eb6e2000-70eb6e6000 rw-p 00000000 00:05 10270963 [anon:dalvik-thread local mark stack]
+70eb6e6000-70eb6ea000 rw-p 00000000 00:05 10270962 [anon:dalvik-thread local mark stack]
+70eb6ea000-70eb6ee000 rw-p 00000000 00:05 10270961 [anon:dalvik-thread local mark stack]
+70eb6ee000-70eb6f2000 rw-p 00000000 00:05 10270960 [anon:dalvik-thread local mark stack]
+70eb6f2000-70eb6f6000 rw-p 00000000 00:05 10270959 [anon:dalvik-thread local mark stack]
+70eb6f6000-70eb6fa000 rw-p 00000000 00:05 10270958 [anon:dalvik-thread local mark stack]
+70eb6fa000-70eb6fe000 rw-p 00000000 00:05 10270957 [anon:dalvik-thread local mark stack]
+70eb6fe000-70eb702000 rw-p 00000000 00:05 10270956 [anon:dalvik-thread local mark stack]
+70eb702000-70eb706000 rw-p 00000000 00:05 10270955 [anon:dalvik-thread local mark stack]
+70eb706000-70eb70a000 rw-p 00000000 00:05 10270954 [anon:dalvik-thread local mark stack]
+70eb70a000-70eb70e000 rw-p 00000000 00:05 10270953 [anon:dalvik-thread local mark stack]
+70eb70e000-70eb712000 rw-p 00000000 00:05 10270952 [anon:dalvik-thread local mark stack]
+70eb712000-70eb71a000 r-xp 00000000 fc:00 2652 /system/lib64/libcamera_metadata.so
+70eb71a000-70eb72f000 ---p 00000000 00:00 0
+70eb72f000-70eb730000 r--p 0000f000 fc:00 2652 /system/lib64/libcamera_metadata.so
+70eb730000-70eb732000 rw-p 00010000 fc:00 2652 /system/lib64/libcamera_metadata.so
+70eb732000-70eb734000 r--s 00000000 fc:00 131 /system/fonts/NotoSansHanunoo-Regular.ttf
+70eb734000-70eb738000 rw-p 00000000 00:05 10270951 [anon:dalvik-thread local mark stack]
+70eb738000-70eb73c000 rw-p 00000000 00:05 10270950 [anon:dalvik-thread local mark stack]
+70eb73c000-70eb740000 rw-p 00000000 00:05 10270949 [anon:dalvik-thread local mark stack]
+70eb740000-70eb744000 rw-p 00000000 00:05 10270948 [anon:dalvik-thread local mark stack]
+70eb744000-70eb748000 rw-p 00000000 00:05 10270947 [anon:dalvik-thread local mark stack]
+70eb748000-70eb74c000 rw-p 00000000 00:05 10270946 [anon:dalvik-thread local mark stack]
+70eb74c000-70eb750000 rw-p 00000000 00:05 10270945 [anon:dalvik-thread local mark stack]
+70eb750000-70eb754000 rw-p 00000000 00:05 10270944 [anon:dalvik-thread local mark stack]
+70eb754000-70eb758000 rw-p 00000000 00:05 10270943 [anon:dalvik-thread local mark stack]
+70eb758000-70eb75c000 rw-p 00000000 00:05 10270942 [anon:dalvik-thread local mark stack]
+70eb75c000-70eb760000 rw-p 00000000 00:05 10270941 [anon:dalvik-thread local mark stack]
+70eb760000-70eb764000 rw-p 00000000 00:05 10270940 [anon:dalvik-thread local mark stack]
+70eb764000-70eb767000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb767000-70eb768000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb768000-70eb76c000 rw-p 00000000 00:05 10270939 [anon:dalvik-thread local mark stack]
+70eb76c000-70eb770000 rw-p 00000000 00:05 10270938 [anon:dalvik-thread local mark stack]
+70eb770000-70eb771000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb771000-70eb774000 r--s 00000000 fc:00 231 /system/fonts/NotoSansDeseret-Regular.ttf
+70eb774000-70eb778000 rw-p 00000000 00:05 10270937 [anon:dalvik-thread local mark stack]
+70eb778000-70eb77c000 rw-p 00000000 00:05 10270936 [anon:dalvik-thread local mark stack]
+70eb77c000-70eb780000 rw-p 00000000 00:05 10270935 [anon:dalvik-thread local mark stack]
+70eb780000-70eb784000 rw-p 00000000 00:05 10270934 [anon:dalvik-thread local mark stack]
+70eb784000-70eb788000 rw-p 00000000 00:05 10270933 [anon:dalvik-thread local mark stack]
+70eb788000-70eb78c000 rw-p 00000000 00:05 10270932 [anon:dalvik-thread local mark stack]
+70eb78c000-70eb790000 rw-p 00000000 00:05 10270931 [anon:dalvik-thread local mark stack]
+70eb790000-70eb794000 rw-p 00000000 00:05 10270930 [anon:dalvik-thread local mark stack]
+70eb794000-70eb798000 rw-p 00000000 00:05 10270929 [anon:dalvik-thread local mark stack]
+70eb798000-70eb79c000 rw-p 00000000 00:05 10270928 [anon:dalvik-thread local mark stack]
+70eb79c000-70eb7a0000 rw-p 00000000 00:05 10270927 [anon:dalvik-thread local mark stack]
+70eb7a0000-70eb7a1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7a1000-70eb7a3000 r--s 00000000 fc:00 176 /system/fonts/NotoSansGothic-Regular.ttf
+70eb7a3000-70eb7a7000 rw-p 00000000 00:05 10270926 [anon:dalvik-thread local mark stack]
+70eb7a7000-70eb7a8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7a8000-70eb7a9000 r--s 00000000 fc:00 1109 /system/usr/hyphen-data/hyph-gu.hyb
+70eb7a9000-70eb7ad000 rw-p 00000000 00:05 10270925 [anon:dalvik-thread local mark stack]
+70eb7ad000-70eb7ae000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7ae000-70eb7af000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb7af000-70eb7b0000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7b0000-70eb7b2000 r--s 00000000 fc:00 191 /system/fonts/NotoSansCypriot-Regular.ttf
+70eb7b2000-70eb7b6000 rw-p 00000000 00:05 10270924 [anon:dalvik-thread local mark stack]
+70eb7b6000-70eb7ba000 rw-p 00000000 00:05 10270923 [anon:dalvik-thread local mark stack]
+70eb7ba000-70eb7be000 rw-p 00000000 00:05 10270922 [anon:dalvik-thread local mark stack]
+70eb7be000-70eb7c2000 rw-p 00000000 00:05 10270921 [anon:dalvik-thread local mark stack]
+70eb7c2000-70eb7c6000 rw-p 00000000 00:05 10270920 [anon:dalvik-thread local mark stack]
+70eb7c6000-70eb7ca000 rw-p 00000000 00:05 10270919 [anon:dalvik-thread local mark stack]
+70eb7ca000-70eb7ce000 rw-p 00000000 00:05 10270918 [anon:dalvik-thread local mark stack]
+70eb7ce000-70eb7d2000 rw-p 00000000 00:05 10270917 [anon:dalvik-thread local mark stack]
+70eb7d2000-70eb7d6000 rw-p 00000000 00:05 10270916 [anon:dalvik-thread local mark stack]
+70eb7d6000-70eb7d7000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70eb7d7000-70eb7db000 rw-p 00000000 00:05 10270915 [anon:dalvik-thread local mark stack]
+70eb7db000-70eb7df000 rw-p 00000000 00:05 10270914 [anon:dalvik-thread local mark stack]
+70eb7df000-70eb7e3000 rw-p 00000000 00:05 10270913 [anon:dalvik-thread local mark stack]
+70eb7e3000-70eb7e7000 rw-p 00000000 00:05 10270912 [anon:dalvik-thread local mark stack]
+70eb7e7000-70eb7e8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7e8000-70eb7ea000 r--s 00000000 fc:00 174 /system/fonts/NotoSansCarian-Regular.ttf
+70eb7ea000-70eb7ee000 rw-p 00000000 00:05 10270911 [anon:dalvik-thread local mark stack]
+70eb7ee000-70eb7f2000 rw-p 00000000 00:05 10270910 [anon:dalvik-thread local mark stack]
+70eb7f2000-70eb7f6000 rw-p 00000000 00:05 10270909 [anon:dalvik-thread local mark stack]
+70eb7f6000-70eb7f7000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb7f7000-70eb7f8000 r--s 00000000 fc:00 1096 /system/usr/hyphen-data/hyph-eu.hyb
+70eb7f8000-70eb7fc000 rw-p 00000000 00:05 10270908 [anon:dalvik-thread local mark stack]
+70eb7fc000-70eb800000 rw-p 00000000 00:05 10270907 [anon:dalvik-thread local mark stack]
+70eb800000-70eb804000 rw-p 00000000 00:05 10270906 [anon:dalvik-thread local mark stack]
+70eb804000-70eb808000 rw-p 00000000 00:05 10270905 [anon:dalvik-thread local mark stack]
+70eb808000-70eb80c000 rw-p 00000000 00:05 10270904 [anon:dalvik-thread local mark stack]
+70eb80c000-70eb810000 rw-p 00000000 00:05 10270903 [anon:dalvik-thread local mark stack]
+70eb810000-70eb814000 rw-p 00000000 00:05 10270902 [anon:dalvik-thread local mark stack]
+70eb814000-70eb815000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70eb815000-70eb819000 rw-p 00000000 00:05 10270901 [anon:dalvik-thread local mark stack]
+70eb819000-70eb81d000 rw-p 00000000 00:05 10270900 [anon:dalvik-thread local mark stack]
+70eb81d000-70eb81e000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb81e000-70eb822000 rw-p 00000000 00:05 10270899 [anon:dalvik-thread local mark stack]
+70eb822000-70eb826000 rw-p 00000000 00:05 10270898 [anon:dalvik-thread local mark stack]
+70eb826000-70eb82a000 rw-p 00000000 00:05 10270897 [anon:dalvik-thread local mark stack]
+70eb82a000-70eb82e000 rw-p 00000000 00:05 10270896 [anon:dalvik-thread local mark stack]
+70eb82e000-70eb832000 rw-p 00000000 00:05 10270895 [anon:dalvik-thread local mark stack]
+70eb832000-70eb836000 rw-p 00000000 00:05 10270894 [anon:dalvik-thread local mark stack]
+70eb836000-70eb837000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb837000-70eb83b000 rw-p 00000000 00:05 10270893 [anon:dalvik-thread local mark stack]
+70eb83b000-70eb83f000 rw-p 00000000 00:05 10270892 [anon:dalvik-thread local mark stack]
+70eb83f000-70eb843000 rw-p 00000000 00:05 10270891 [anon:dalvik-thread local mark stack]
+70eb843000-70eb847000 rw-p 00000000 00:05 10270890 [anon:dalvik-thread local mark stack]
+70eb847000-70eb84b000 rw-p 00000000 00:05 10270889 [anon:dalvik-thread local mark stack]
+70eb84b000-70eb84f000 rw-p 00000000 00:05 10270888 [anon:dalvik-thread local mark stack]
+70eb84f000-70eb850000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb850000-70eb854000 rw-p 00000000 00:05 10270887 [anon:dalvik-thread local mark stack]
+70eb854000-70eb858000 rw-p 00000000 00:05 10270886 [anon:dalvik-thread local mark stack]
+70eb858000-70eb85c000 rw-p 00000000 00:05 10270885 [anon:dalvik-thread local mark stack]
+70eb85c000-70eb860000 rw-p 00000000 00:05 10270884 [anon:dalvik-thread local mark stack]
+70eb860000-70eb864000 rw-p 00000000 00:05 10270883 [anon:dalvik-thread local mark stack]
+70eb864000-70eb868000 rw-p 00000000 00:05 10270882 [anon:dalvik-thread local mark stack]
+70eb868000-70eb869000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb869000-70eb86d000 rw-p 00000000 00:05 10270881 [anon:dalvik-thread local mark stack]
+70eb86d000-70eb871000 rw-p 00000000 00:05 10270880 [anon:dalvik-thread local mark stack]
+70eb871000-70eb875000 rw-p 00000000 00:05 10270879 [anon:dalvik-thread local mark stack]
+70eb875000-70eb879000 rw-p 00000000 00:05 10270878 [anon:dalvik-thread local mark stack]
+70eb879000-70eb87d000 rw-p 00000000 00:05 10270877 [anon:dalvik-thread local mark stack]
+70eb87d000-70eb881000 rw-p 00000000 00:05 10270876 [anon:dalvik-thread local mark stack]
+70eb881000-70eb885000 rw-p 00000000 00:05 10270875 [anon:dalvik-thread local mark stack]
+70eb885000-70eb889000 rw-p 00000000 00:05 10270874 [anon:dalvik-thread local mark stack]
+70eb889000-70eb88d000 rw-p 00000000 00:05 10270873 [anon:dalvik-thread local mark stack]
+70eb88d000-70eb891000 rw-p 00000000 00:05 10270872 [anon:dalvik-thread local mark stack]
+70eb891000-70eb892000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb892000-70eb896000 rw-p 00000000 00:05 10270871 [anon:dalvik-thread local mark stack]
+70eb896000-70eb89a000 rw-p 00000000 00:05 10270870 [anon:dalvik-thread local mark stack]
+70eb89a000-70eb89b000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70eb89b000-70eb89c000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70eb89c000-70eb8a0000 rw-p 00000000 00:05 10270869 [anon:dalvik-thread local mark stack]
+70eb8a0000-70eb8a4000 rw-p 00000000 00:05 10270868 [anon:dalvik-thread local mark stack]
+70eb8a4000-70eb8a5000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70eb8a5000-70eb8a9000 rw-p 00000000 00:05 10270867 [anon:dalvik-thread local mark stack]
+70eb8a9000-70eb8ad000 rw-p 00000000 00:05 10270866 [anon:dalvik-thread local mark stack]
+70eb8ad000-70eb8b1000 rw-p 00000000 00:05 10270865 [anon:dalvik-thread local mark stack]
+70eb8b1000-70eb8b5000 rw-p 00000000 00:05 10270864 [anon:dalvik-thread local mark stack]
+70eb8b5000-70eb8b9000 rw-p 00000000 00:05 10270863 [anon:dalvik-thread local mark stack]
+70eb8b9000-70eb8bd000 rw-p 00000000 00:05 10270862 [anon:dalvik-thread local mark stack]
+70eb8bd000-70eb8be000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb8be000-70eb8c1000 r--s 00000000 fc:00 168 /system/fonts/NotoSansAvestan-Regular.ttf
+70eb8c1000-70eb8c5000 rw-p 00000000 00:05 10270861 [anon:dalvik-thread local mark stack]
+70eb8c5000-70eb8c9000 rw-p 00000000 00:05 10270860 [anon:dalvik-thread local mark stack]
+70eb8c9000-70eb8cd000 rw-p 00000000 00:05 10270859 [anon:dalvik-thread local mark stack]
+70eb8cd000-70eb8d1000 rw-p 00000000 00:05 10270858 [anon:dalvik-thread local mark stack]
+70eb8d1000-70eb8d5000 rw-p 00000000 00:05 10270857 [anon:dalvik-thread local mark stack]
+70eb8d5000-70eb8d7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb8d7000-70eb8db000 rw-p 00000000 00:05 10270856 [anon:dalvik-thread local mark stack]
+70eb8db000-70eb8df000 rw-p 00000000 00:05 10270855 [anon:dalvik-thread local mark stack]
+70eb8df000-70eb8e3000 rw-p 00000000 00:05 10270854 [anon:dalvik-thread local mark stack]
+70eb8e3000-70eb8e7000 rw-p 00000000 00:05 10270853 [anon:dalvik-thread local mark stack]
+70eb8e7000-70eb8eb000 rw-p 00000000 00:05 10270852 [anon:dalvik-thread local mark stack]
+70eb8eb000-70eb8ec000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb8ec000-70eb8ed000 r--s 00000000 fc:00 1099 /system/usr/hyphen-data/hyph-bn.hyb
+70eb8ed000-70eb8f1000 rw-p 00000000 00:05 10270851 [anon:dalvik-thread local mark stack]
+70eb8f1000-70eb8f5000 rw-p 00000000 00:05 10270850 [anon:dalvik-thread local mark stack]
+70eb8f5000-70eb8f9000 rw-p 00000000 00:05 10270849 [anon:dalvik-thread local mark stack]
+70eb8f9000-70eb8fd000 rw-p 00000000 00:05 10270848 [anon:dalvik-thread local mark stack]
+70eb8fd000-70eb901000 rw-p 00000000 00:05 10270847 [anon:dalvik-thread local mark stack]
+70eb901000-70eb905000 rw-p 00000000 00:05 10270846 [anon:dalvik-thread local mark stack]
+70eb905000-70eb909000 rw-p 00000000 00:05 10270845 [anon:dalvik-thread local mark stack]
+70eb909000-70eb90d000 rw-p 00000000 00:05 10270844 [anon:dalvik-thread local mark stack]
+70eb90d000-70eb911000 rw-p 00000000 00:05 10270843 [anon:dalvik-thread local mark stack]
+70eb911000-70eb915000 rw-p 00000000 00:05 10270842 [anon:dalvik-thread local mark stack]
+70eb915000-70eb916000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb916000-70eb917000 r--s 00000000 fc:00 1114 /system/usr/hyphen-data/hyph-bg.hyb
+70eb917000-70eb91b000 rw-p 00000000 00:05 10270841 [anon:dalvik-thread local mark stack]
+70eb91b000-70eb91c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb91c000-70eb91d000 r--s 00000000 fc:00 1133 /system/usr/hyphen-data/hyph-as.hyb
+70eb91d000-70eb921000 rw-p 00000000 00:05 10270840 [anon:dalvik-thread local mark stack]
+70eb921000-70eb925000 rw-p 00000000 00:05 10270839 [anon:dalvik-thread local mark stack]
+70eb925000-70eb926000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70eb926000-70eb927000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb927000-70eb929000 r--s 00000000 fc:00 203 /system/fonts/NotoSansBuhid-Regular.ttf
+70eb929000-70eb92d000 rw-p 00000000 00:05 10270838 [anon:dalvik-thread local mark stack]
+70eb92d000-70eb931000 rw-p 00000000 00:05 10270837 [anon:dalvik-thread local mark stack]
+70eb931000-70eb935000 rw-p 00000000 00:05 10270836 [anon:dalvik-thread local mark stack]
+70eb935000-70eb939000 rw-p 00000000 00:05 10270835 [anon:dalvik-thread local mark stack]
+70eb939000-70eb93d000 rw-p 00000000 00:05 10270834 [anon:dalvik-thread local mark stack]
+70eb93d000-70eb941000 rw-p 00000000 00:05 10270833 [anon:dalvik-thread local mark stack]
+70eb941000-70eb945000 rw-p 00000000 00:05 10270832 [anon:dalvik-thread local mark stack]
+70eb945000-70eb949000 rw-p 00000000 00:05 10270831 [anon:dalvik-thread local mark stack]
+70eb949000-70eb94d000 rw-p 00000000 00:05 10270830 [anon:dalvik-thread local mark stack]
+70eb94d000-70eb951000 rw-p 00000000 00:05 10270829 [anon:dalvik-thread local mark stack]
+70eb951000-70eb991000 rw-p 00000000 00:05 10270722 [anon:dalvik-mark stack]
+70eb991000-70eb992000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb992000-70eb996000 rw-p 00000000 00:05 10270828 [anon:dalvik-thread local mark stack]
+70eb996000-70eb99a000 rw-p 00000000 00:05 10270827 [anon:dalvik-thread local mark stack]
+70eb99a000-70eb99e000 rw-p 00000000 00:05 10270826 [anon:dalvik-thread local mark stack]
+70eb99e000-70eb9a2000 rw-p 00000000 00:05 10270825 [anon:dalvik-thread local mark stack]
+70eb9a2000-70eb9a4000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb9a4000-70eb9a8000 rw-p 00000000 00:05 10270824 [anon:dalvik-thread local mark stack]
+70eb9a8000-70eb9ac000 rw-p 00000000 00:05 10270823 [anon:dalvik-thread local mark stack]
+70eb9ac000-70eb9b0000 rw-p 00000000 00:05 10270822 [anon:dalvik-thread local mark stack]
+70eb9b0000-70eb9b1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb9b1000-70eb9b2000 r--s 00021000 fc:01 1180 /vendor/overlay/framework-res__auto_generated_rro.apk
+70eb9b2000-70eb9b6000 rw-p 00000000 00:05 10270821 [anon:dalvik-thread local mark stack]
+70eb9b6000-70eb9ba000 rw-p 00000000 00:05 10270820 [anon:dalvik-thread local mark stack]
+70eb9ba000-70eb9be000 rw-p 00000000 00:05 10270819 [anon:dalvik-thread local mark stack]
+70eb9be000-70eb9c2000 rw-p 00000000 00:05 10270818 [anon:dalvik-thread local mark stack]
+70eb9c2000-70eb9c6000 rw-p 00000000 00:05 10270817 [anon:dalvik-thread local mark stack]
+70eb9c6000-70eb9ca000 rw-p 00000000 00:05 10270816 [anon:dalvik-thread local mark stack]
+70eb9ca000-70eb9ce000 rw-p 00000000 00:05 10270815 [anon:dalvik-thread local mark stack]
+70eb9ce000-70eb9cf000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb9cf000-70eb9d1000 r--s 00000000 fc:00 213 /system/fonts/NotoSansBuginese-Regular.ttf
+70eb9d1000-70eb9d5000 rw-p 00000000 00:05 10270814 [anon:dalvik-thread local mark stack]
+70eb9d5000-70eb9d9000 rw-p 00000000 00:05 10270813 [anon:dalvik-thread local mark stack]
+70eb9d9000-70eb9dd000 rw-p 00000000 00:05 10270812 [anon:dalvik-thread local mark stack]
+70eb9dd000-70eb9e1000 rw-p 00000000 00:05 10270811 [anon:dalvik-thread local mark stack]
+70eb9e1000-70eb9e5000 rw-p 00000000 00:05 10270810 [anon:dalvik-thread local mark stack]
+70eb9e5000-70eb9e9000 rw-p 00000000 00:05 10270809 [anon:dalvik-thread local mark stack]
+70eb9e9000-70eb9ed000 rw-p 00000000 00:05 10270808 [anon:dalvik-thread local mark stack]
+70eb9ed000-70eb9f1000 rw-p 00000000 00:05 10270807 [anon:dalvik-thread local mark stack]
+70eb9f1000-70eb9f5000 rw-p 00000000 00:05 10270806 [anon:dalvik-thread local mark stack]
+70eb9f5000-70eb9f6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb9f6000-70eb9f8000 rw-p 00000000 00:05 10271002 [anon:dalvik-indirect ref table]
+70eb9f8000-70eb9fc000 rw-p 00000000 00:05 10270805 [anon:dalvik-thread local mark stack]
+70eb9fc000-70eb9fd000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb9fd000-70eb9ff000 rw-p 00000000 00:05 10270999 [anon:dalvik-indirect ref table]
+70eb9ff000-70eba00000 r--s 00000000 fc:00 983 /system/framework/com.google.vr.platform.jar
+70eba00000-70eba04000 rw-p 00000000 00:05 10270804 [anon:dalvik-thread local mark stack]
+70eba04000-70eba08000 rw-p 00000000 00:05 10270803 [anon:dalvik-thread local mark stack]
+70eba08000-70eba0c000 rw-p 00000000 00:05 10270802 [anon:dalvik-thread local mark stack]
+70eba0c000-70eba10000 rw-p 00000000 00:05 10270801 [anon:dalvik-thread local mark stack]
+70eba10000-70eba14000 rw-p 00000000 00:05 10270800 [anon:dalvik-thread local mark stack]
+70eba14000-70eba18000 rw-p 00000000 00:05 10270799 [anon:dalvik-thread local mark stack]
+70eba18000-70eba1c000 rw-p 00000000 00:05 10270798 [anon:dalvik-thread local mark stack]
+70eba1c000-70eba20000 rw-p 00000000 00:05 10270797 [anon:dalvik-thread local mark stack]
+70eba20000-70eba24000 rw-p 00000000 00:05 10270796 [anon:dalvik-thread local mark stack]
+70eba24000-70eba28000 rw-p 00000000 00:05 10270795 [anon:dalvik-thread local mark stack]
+70eba28000-70eba2c000 rw-p 00000000 00:05 10270794 [anon:dalvik-thread local mark stack]
+70eba2c000-70eba2d000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eba2d000-70eba2e000 r--s 00000000 fc:00 881 /system/framework/android.test.base.jar
+70eba2e000-70eba2f000 r--s 00000000 fc:00 707 /system/framework/framework-oahl-backward-compatibility.jar
+70eba2f000-70eba30000 r--s 00000000 fc:00 705 /system/framework/android.hidl.manager-V1.0-java.jar
+70eba30000-70eba34000 rw-p 00000000 00:05 10270793 [anon:dalvik-thread local mark stack]
+70eba34000-70eba38000 rw-p 00000000 00:05 10270792 [anon:dalvik-thread local mark stack]
+70eba38000-70eba3c000 rw-p 00000000 00:05 10270791 [anon:dalvik-thread local mark stack]
+70eba3c000-70eba40000 rw-p 00000000 00:05 10270790 [anon:dalvik-thread local mark stack]
+70eba40000-70eba44000 rw-p 00000000 00:05 10270789 [anon:dalvik-thread local mark stack]
+70eba44000-70eba48000 rw-p 00000000 00:05 10270788 [anon:dalvik-thread local mark stack]
+70eba48000-70eba4c000 rw-p 00000000 00:05 10270787 [anon:dalvik-thread local mark stack]
+70eba4c000-70eba50000 rw-p 00000000 00:05 10270786 [anon:dalvik-thread local mark stack]
+70eba50000-70eba52000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eba52000-70eba53000 r--s 00000000 fc:00 971 /system/framework/android.hidl.base-V1.0-java.jar
+70eba53000-70eba57000 rw-p 00000000 00:05 10270785 [anon:dalvik-thread local mark stack]
+70eba57000-70eba5b000 rw-p 00000000 00:05 10270784 [anon:dalvik-thread local mark stack]
+70eba5b000-70eba5f000 rw-p 00000000 00:05 10270783 [anon:dalvik-thread local mark stack]
+70eba5f000-70eba63000 rw-p 00000000 00:05 10270782 [anon:dalvik-thread local mark stack]
+70eba63000-70eba64000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eba64000-70eba65000 r--s 00000000 fc:00 889 /system/framework/ims-common.jar
+70eba65000-70eba69000 rw-p 00000000 00:05 10270781 [anon:dalvik-thread local mark stack]
+70eba69000-70eba6d000 rw-p 00000000 00:05 10270780 [anon:dalvik-thread local mark stack]
+70eba6d000-70eba71000 rw-p 00000000 00:05 10270779 [anon:dalvik-thread local mark stack]
+70eba71000-70eba75000 rw-p 00000000 00:05 10270778 [anon:dalvik-thread local mark stack]
+70eba75000-70eba95000 rw-p 00000000 00:05 10267647 [anon:dalvik-large marked objects]
+70eba95000-70ebab5000 rw-p 00000000 00:05 10267646 [anon:dalvik-large live objects]
+70ebab5000-70ebab6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebab6000-70ebab7000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebab7000-70ebabb000 rw-p 00000000 00:05 10270777 [anon:dalvik-thread local mark stack]
+70ebabb000-70ebadb000 r--s 00000000 00:10 16603 /dev/__properties__/u:object_r:exported_fingerprint_prop:s0
+70ebadb000-70ebadc000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebadc000-70ebadd000 r--s 00000000 fc:00 878 /system/framework/voip-common.jar
+70ebadd000-70ebadf000 rw-p 00000000 00:05 10270995 [anon:dalvik-indirect ref table]
+70ebadf000-70ebae3000 rw-p 00000000 00:05 10270776 [anon:dalvik-thread local mark stack]
+70ebae3000-70ebae7000 rw-p 00000000 00:05 10270775 [anon:dalvik-thread local mark stack]
+70ebae7000-70ebaeb000 rw-p 00000000 00:05 10270774 [anon:dalvik-thread local mark stack]
+70ebaeb000-70ebaef000 rw-p 00000000 00:05 10270773 [anon:dalvik-thread local mark stack]
+70ebaef000-70ebb0f000 r--s 00000000 00:10 16582 /dev/__properties__/u:object_r:debug_prop:s0
+70ebb0f000-70ebb10000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebb10000-70ebb11000 r--s 00000000 fc:00 703 /system/framework/telephony-common.jar
+70ebb11000-70ebb13000 rw-p 00000000 00:05 10270994 [anon:dalvik-indirect ref table]
+70ebb13000-70ebb17000 rw-p 00000000 00:05 10270772 [anon:dalvik-thread local mark stack]
+70ebb17000-70ebb19000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebb19000-70ebb1d000 rw-p 00000000 00:05 10270771 [anon:dalvik-thread local mark stack]
+70ebb1d000-70ebb3d000 r--s 00000000 00:10 16600 /dev/__properties__/u:object_r:exported_default_prop:s0
+70ebb3d000-70ebb5d000 r--s 00000000 00:10 16650 /dev/__properties__/u:object_r:system_prop:s0
+70ebb5d000-70ebb7d000 r--s 00000000 00:10 16610 /dev/__properties__/u:object_r:exported_vold_prop:s0
+70ebb7d000-70ebb9d000 r--s 00000000 00:10 16598 /dev/__properties__/u:object_r:exported_config_prop:s0
+70ebb9d000-70ebb9e000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebb9e000-70ebba2000 rw-p 00000000 00:05 10270770 [anon:dalvik-thread local mark stack]
+70ebba2000-70ebba6000 rw-p 00000000 00:05 10270769 [anon:dalvik-thread local mark stack]
+70ebba6000-70ebba7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebba7000-70ebba8000 r--s 00000000 fc:00 1004 /system/framework/framework.jar
+70ebba8000-70ebbac000 rw-p 00000000 00:05 10270768 [anon:dalvik-thread local mark stack]
+70ebbac000-70ebbb0000 rw-p 00000000 00:05 10270767 [anon:dalvik-thread local mark stack]
+70ebbb0000-70ebbb4000 rw-p 00000000 00:05 10270766 [anon:dalvik-thread local mark stack]
+70ebbb4000-70ebbb8000 rw-p 00000000 00:05 10270765 [anon:dalvik-thread local mark stack]
+70ebbb8000-70ebbbc000 rw-p 00000000 00:05 10270764 [anon:dalvik-thread local mark stack]
+70ebbbc000-70ebbc0000 rw-p 00000000 00:05 10270763 [anon:dalvik-thread local mark stack]
+70ebbc0000-70ebbc4000 rw-p 00000000 00:05 10270762 [anon:dalvik-thread local mark stack]
+70ebbc4000-70ebbe4000 r--s 00000000 00:10 16581 /dev/__properties__/u:object_r:dalvik_prop:s0
+70ebbe4000-70ebbe5000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebbe5000-70ebbe6000 r--s 00004000 fc:00 877 /system/framework/apache-xml.jar
+70ebbe6000-70ebbe8000 rw-p 00000000 00:05 10270993 [anon:dalvik-indirect ref table]
+70ebbe8000-70ebbec000 rw-p 00000000 00:05 10270761 [anon:dalvik-thread local mark stack]
+70ebbec000-70ebbf0000 rw-p 00000000 00:05 10270760 [anon:dalvik-thread local mark stack]
+70ebbf0000-70ebbf4000 rw-p 00000000 00:05 10270759 [anon:dalvik-thread local mark stack]
+70ebbf4000-70ebbf8000 rw-p 00000000 00:05 10270758 [anon:dalvik-thread local mark stack]
+70ebbf8000-70ebbf9000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebbf9000-70ebbfa000 r--s 00000000 fc:00 968 /system/framework/bouncycastle.jar
+70ebbfa000-70ebbfc000 rw-p 00000000 00:05 10270992 [anon:dalvik-indirect ref table]
+70ebbfc000-70ebc00000 rw-p 00000000 00:05 10270757 [anon:dalvik-thread local mark stack]
+70ebc00000-70ebc04000 rw-p 00000000 00:05 10270756 [anon:dalvik-thread local mark stack]
+70ebc04000-70ebc08000 rw-p 00000000 00:05 10270755 [anon:dalvik-thread local mark stack]
+70ebc08000-70ebc0c000 rw-p 00000000 00:05 10270754 [anon:dalvik-thread local mark stack]
+70ebc0c000-70ebc10000 rw-p 00000000 00:05 10270753 [anon:dalvik-thread local mark stack]
+70ebc10000-70ebc14000 rw-p 00000000 00:05 10270752 [anon:dalvik-thread local mark stack]
+70ebc14000-70ebc15000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc15000-70ebc16000 r--s 00000000 fc:00 960 /system/framework/okhttp.jar
+70ebc16000-70ebc1a000 rw-p 00000000 00:05 10270751 [anon:dalvik-thread local mark stack]
+70ebc1a000-70ebc1e000 rw-p 00000000 00:05 10270750 [anon:dalvik-thread local mark stack]
+70ebc1e000-70ebc3e000 r--s 00000000 00:10 16584 /dev/__properties__/u:object_r:default_prop:s0
+70ebc3e000-70ebc3f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc3f000-70ebc40000 r--s 00000000 fc:00 974 /system/framework/conscrypt.jar
+70ebc40000-70ebc42000 rw-p 00000000 00:05 10269719 [anon:dalvik-indirect ref table]
+70ebc42000-70ebc46000 rw-p 00000000 00:05 10270749 [anon:dalvik-thread local mark stack]
+70ebc46000-70ebc4a000 rw-p 00000000 00:05 10270748 [anon:dalvik-thread local mark stack]
+70ebc4a000-70ebc4e000 rw-p 00000000 00:05 10270747 [anon:dalvik-thread local mark stack]
+70ebc4e000-70ebc4f000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc4f000-70ebc51000 rw-p 00000000 00:05 10269718 [anon:dalvik-indirect ref table]
+70ebc51000-70ebc55000 rw-p 00000000 00:05 10270746 [anon:dalvik-thread local mark stack]
+70ebc55000-70ebc59000 rw-p 00000000 00:05 10270745 [anon:dalvik-thread local mark stack]
+70ebc59000-70ebc5d000 rw-p 00000000 00:05 10270744 [anon:dalvik-thread local mark stack]
+70ebc5d000-70ebc7d000 r--s 00000000 00:10 16599 /dev/__properties__/u:object_r:exported_dalvik_prop:s0
+70ebc7d000-70ebc7e000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc7e000-70ebc7f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc7f000-70ebc80000 r--s 00004000 fc:00 963 /system/framework/core-libart.jar
+70ebc80000-70ebc81000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc81000-70ebc85000 rw-p 00000000 00:05 10270743 [anon:dalvik-thread local mark stack]
+70ebc85000-70ebc89000 rw-p 00000000 00:05 10270742 [anon:dalvik-thread local mark stack]
+70ebc89000-70ebc8a000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebc8a000-70ebc8c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc8c000-70ebc8d000 r--s 0001e000 fc:00 699 /system/framework/core-oj.jar
+70ebc8d000-70ebc91000 rw-p 00000000 00:05 10270741 [anon:dalvik-thread local mark stack]
+70ebc91000-70ebc95000 rw-p 00000000 00:05 10270740 [anon:dalvik-thread local mark stack]
+70ebc95000-70ebc99000 rw-p 00000000 00:05 10270739 [anon:dalvik-thread local mark stack]
+70ebc99000-70ebc9b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc9b000-70ebc9c000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc9c000-70ebca0000 rw-p 00000000 00:05 10270738 [anon:dalvik-thread local mark stack]
+70ebca0000-70ebca4000 rw-p 00000000 00:05 10270737 [anon:dalvik-thread local mark stack]
+70ebca4000-70ebca8000 rw-p 00000000 00:05 10270736 [anon:dalvik-thread local mark stack]
+70ebca8000-70ebcac000 rw-p 00000000 00:05 10270735 [anon:dalvik-thread local mark stack]
+70ebcac000-70ebcb0000 rw-p 00000000 00:05 10270734 [anon:dalvik-thread local mark stack]
+70ebcb0000-70ebcd0000 r--s 00000000 00:10 16592 /dev/__properties__/u:object_r:exported2_system_prop:s0
+70ebcd0000-70ebcd1000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebcd1000-70ebcd5000 rw-p 00000000 00:05 10270733 [anon:dalvik-thread local mark stack]
+70ebcd5000-70ebcd9000 rw-p 00000000 00:05 10270732 [anon:dalvik-thread local mark stack]
+70ebcd9000-70ebcdd000 rw-p 00000000 00:05 10270731 [anon:dalvik-thread local mark stack]
+70ebcdd000-70ebce1000 rw-p 00000000 00:05 10270730 [anon:dalvik-thread local mark stack]
+70ebce1000-70ebce5000 rw-p 00000000 00:05 10270729 [anon:dalvik-thread local mark stack]
+70ebce5000-70ebce9000 rw-p 00000000 00:05 10270728 [anon:dalvik-thread local mark stack]
+70ebce9000-70ebcea000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebcea000-70ebcec000 rw-p 00000000 00:05 10270987 [anon:dalvik-indirect ref table]
+70ebcec000-70ebcf9000 r--p 00646000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70ebcf9000-70ebd19000 r--s 00000000 00:10 16620 /dev/__properties__/u:object_r:log_tag_prop:s0
+70ebd19000-70ebd39000 r--s 00000000 00:10 16621 /dev/__properties__/u:object_r:logd_prop:s0
+70ebd39000-70ebd3a000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebd3a000-70ebd3b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd3b000-70ebd3f000 rw-p 00000000 00:05 10270727 [anon:dalvik-thread local mark stack]
+70ebd3f000-70ebd40000 r--p 00002000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70ebd40000-70ebd41000 r--p 00005000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70ebd41000-70ebd42000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd42000-70ebd43000 r--p 00001000 103:1d 639550 /data/dalvik-cache/arm64/system@framework@boot-framework-oahl-backward-compatibility.art
+70ebd43000-70ebd44000 r--p 00005000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+70ebd44000-70ebd45000 r--p 00003000 103:1d 639544 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.base-V1.0-java.art
+70ebd45000-70ebd46000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd46000-70ebd47000 r--p 0000f000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70ebd47000-70ebd48000 r--p 0000d000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70ebd48000-70ebd4a000 r--p 0005e000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70ebd4a000-70ebd4b000 r--p 00040000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+70ebd4b000-70ebd4c000 r--p 0004a000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+70ebd4c000-70ebd4d000 r--p 00046000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+70ebd4d000-70ebd4e000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd4e000-70ebd53000 r--p 00225000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+70ebd53000-70ebd5a000 rw-p 00000000 fc:00 583 /system/etc/event-log-tags
+70ebd5a000-70ebd5b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd5b000-70ebd5c000 r--p 0002e000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+70ebd5c000-70ebd5d000 r--p 00035000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+70ebd5d000-70ebd5f000 r--p 000d0000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+70ebd5f000-70ebd62000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70ebd62000-70ebd63000 rw-p 00000000 00:00 0
+70ebd63000-70ebd83000 r--s 00000000 00:10 16590 /dev/__properties__/u:object_r:exported2_default_prop:s0
+70ebd83000-70ebd84000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebd84000-70ebd89000 rw-p 00000000 00:00 0
+70ebd89000-70ebda9000 r--s 00000000 00:10 16669 /dev/__properties__/properties_serial
+70ebda9000-70ebdb3000 r--s 00000000 00:10 16560 /dev/__properties__/property_info
+70ebdb3000-70ebdb4000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdb4000-70ebdb5000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdb5000-70ebdb7000 rw-p 00000000 00:00 0 [anon:System property context nodes]
+70ebdb7000-70ebdb8000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdb8000-70ebdba000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdba000-70ebdbb000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebdbb000-70ebdbd000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdbd000-70ebdbe000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70ebdbe000-70ebdbf000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebdbf000-70ebdc0000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdc0000-70ebdc1000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdc1000-70ebdc2000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdc2000-70ebdc3000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdc3000-70ebdc5000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdc5000-70ebde5000 r--s 00000000 00:10 16600 /dev/__properties__/u:object_r:exported_default_prop:s0
+70ebde5000-70ebde6000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebde6000-70ebde8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebde8000-70ebe08000 r--s 00000000 00:10 16582 /dev/__properties__/u:object_r:debug_prop:s0
+70ebe08000-70ebe09000 ---p 00000000 00:00 0
+70ebe09000-70ebe0a000 rw-p 00000000 00:00 0
+70ebe0a000-70ebe0b000 ---p 00000000 00:00 0
+70ebe0b000-70ebe2b000 r--s 00000000 00:10 16669 /dev/__properties__/properties_serial
+70ebe2b000-70ebe2d000 rw-p 00000000 00:00 0 [anon:System property context nodes]
+70ebe2d000-70ebf55000 r-xp 00000000 fc:00 3184 /system/bin/linker64
+70ebf55000-70ebf5f000 r--s 00000000 00:10 16560 /dev/__properties__/property_info
+70ebf5f000-70ebf60000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebf60000-70ebf61000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebf61000-70ebf62000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebf62000-70ebf63000 rw-p 00000000 00:00 0 [anon:arc4random data]
+70ebf63000-70ebf64000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebf64000-70ebf65000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70ebf65000-70ebf66000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70ebf66000-70ebf6a000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70ebf6a000-70ebf6b000 rw-p 00000000 00:00 0 [anon:arc4random data]
+70ebf6b000-70ebf6c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70ebf6c000-70ebf6f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70ebf6f000-70ebf70000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70ebf70000-70ebf71000 r--p 00000000 00:00 0 [vvar]
+70ebf71000-70ebf72000 r-xp 00000000 00:00 0 [vdso]
+70ebf72000-70ebf7d000 r--p 00135000 fc:00 3184 /system/bin/linker64
+70ebf7d000-70ebf7e000 rw-p 00140000 fc:00 3184 /system/bin/linker64
+70ebf7e000-70ebf81000 rw-p 00000000 00:00 0
+70ebf81000-70ebf82000 r--p 00000000 00:00 0
+70ebf82000-70ebf89000 rw-p 00000000 00:00 0
+7fc7df1000-7fc7df2000 ---p 00000000 00:00 0
+7fc7df2000-7fc85f1000 rw-p 00000000 00:00 0 [stack]
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
index 64db09580..de632caa2 100644
--- a/libqtaguid/Android.bp
+++ b/libqtaguid/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libqtaguid_headers",
vendor_available: false,
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 0b4b640ad..135904bd0 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -1,17 +1,11 @@
// Copyright 2010 The Android Open Source Project
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libsparse",
host_supported: true,
ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
unique_host_soname: true,
- vendor_available: true,
srcs: [
"backed_block.cpp",
"output_file.cpp",
@@ -32,10 +26,6 @@ cc_library {
enabled: true,
},
},
- apex_available: [
- "//apex_available:platform",
- "com.android.virt",
- ],
}
cc_binary {
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
index 6229e7c6e..f3d8022ac 100644
--- a/libsparse/backed_block.cpp
+++ b/libsparse/backed_block.cpp
@@ -25,7 +25,7 @@
struct backed_block {
unsigned int block;
- uint64_t len;
+ unsigned int len;
enum backed_block_type type;
union {
struct {
@@ -60,7 +60,7 @@ struct backed_block* backed_block_iter_next(struct backed_block* bb) {
return bb->next;
}
-uint64_t backed_block_len(struct backed_block* bb) {
+unsigned int backed_block_len(struct backed_block* bb) {
return bb->len;
}
@@ -270,7 +270,7 @@ static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb)
}
/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
@@ -287,7 +287,7 @@ int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val,
}
/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
@@ -305,7 +305,7 @@ int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t le
/* Queues a chunk of a file on disk to be written to the specified data blocks */
int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
- uint64_t len, unsigned int block) {
+ unsigned int len, unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
return -ENOMEM;
@@ -322,7 +322,7 @@ int backed_block_add_file(struct backed_block_list* bbl, const char* filename, i
}
/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 71a89698e..3a7546065 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -29,18 +29,18 @@ enum backed_block_type {
BACKED_BLOCK_FILL,
};
-int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
unsigned int block);
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
unsigned int block);
int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
- uint64_t len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
+ unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
unsigned int block);
struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
struct backed_block* backed_block_iter_next(struct backed_block* bb);
-uint64_t backed_block_len(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
unsigned int backed_block_block(struct backed_block* bb);
void* backed_block_data(struct backed_block* bb);
const char* backed_block_filename(struct backed_block* bb);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 2f75349a6..3d5fb0c53 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -75,7 +75,8 @@ void sparse_file_destroy(struct sparse_file *s);
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block);
+int sparse_file_add_data(struct sparse_file *s,
+ void *data, unsigned int len, unsigned int block);
/**
* sparse_file_add_fill - associate a fill chunk with a sparse file
@@ -92,8 +93,8 @@ int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsign
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
- unsigned int block);
+int sparse_file_add_fill(struct sparse_file *s,
+ uint32_t fill_val, unsigned int len, unsigned int block);
/**
* sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -115,8 +116,9 @@ int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
- uint64_t len, unsigned int block);
+int sparse_file_add_file(struct sparse_file *s,
+ const char *filename, int64_t file_offset, unsigned int len,
+ unsigned int block);
/**
* sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -141,8 +143,8 @@ int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t fi
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
- unsigned int block);
+int sparse_file_add_fd(struct sparse_file *s,
+ int fd, int64_t file_offset, unsigned int len, unsigned int block);
/**
* sparse_file_write - write a sparse file to a file
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index b2c5407e1..e35cb0d0e 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -35,9 +35,8 @@
#include "sparse_crc32.h"
#include "sparse_format.h"
-#include <android-base/mapped_file.h>
-
#ifndef _WIN32
+#include <sys/mman.h>
#define O_BINARY 0
#else
#define ftruncate64 ftruncate
@@ -46,6 +45,7 @@
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define ftruncate64 ftruncate
+#define mmap64 mmap
#define off64_t off_t
#endif
@@ -65,9 +65,9 @@ struct output_file_ops {
};
struct sparse_file_ops {
- int (*write_data_chunk)(struct output_file* out, uint64_t len, void* data);
- int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
- int (*write_skip_chunk)(struct output_file* out, uint64_t len);
+ int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+ int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file* out, int64_t len);
int (*write_end_chunk)(struct output_file* out);
};
@@ -316,7 +316,7 @@ int read_all(int fd, void* buf, size_t len) {
return 0;
}
-static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
chunk_header_t chunk_header;
int ret;
@@ -340,10 +340,9 @@ static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
return 0;
}
-static int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
+static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
chunk_header_t chunk_header;
- uint64_t rnd_up_len;
- int count;
+ int rnd_up_len, count;
int ret;
/* Round up the fill length to a multiple of the block size */
@@ -371,9 +370,9 @@ static int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32
return 0;
}
-static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* data) {
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
chunk_header_t chunk_header;
- uint64_t rnd_up_len, zero_len;
+ int rnd_up_len, zero_len;
int ret;
/* Round up the data length to a multiple of the block size */
@@ -438,9 +437,9 @@ static struct sparse_file_ops sparse_file_ops = {
.write_end_chunk = write_sparse_end_chunk,
};
-static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
int ret;
- uint64_t rnd_up_len = ALIGN(len, out->block_size);
+ unsigned int rnd_up_len = ALIGN(len, out->block_size);
ret = out->ops->write(out, data, len);
if (ret < 0) {
@@ -454,10 +453,10 @@ static int write_normal_data_chunk(struct output_file* out, uint64_t len, void*
return ret;
}
-static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
+static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
int ret;
unsigned int i;
- uint64_t write_len;
+ unsigned int write_len;
/* Initialize fill_buf with the fill_val */
for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
@@ -465,7 +464,7 @@ static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32
}
while (len) {
- write_len = std::min(len, (uint64_t)out->block_size);
+ write_len = std::min(len, out->block_size);
ret = out->ops->write(out, out->fill_buf, write_len);
if (ret < 0) {
return ret;
@@ -477,7 +476,7 @@ static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32
return 0;
}
-static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
+static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
return out->ops->skip(out, len);
}
@@ -640,24 +639,66 @@ struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t
}
/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file* out, uint64_t len, void* data) {
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
return out->sparse_ops->write_data_chunk(out, len, data);
}
/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
return out->sparse_ops->write_fill_chunk(out, len, fill_val);
}
-int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
- auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
- if (!m) return -errno;
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+ int ret;
+ int64_t aligned_offset;
+ int aligned_diff;
+ uint64_t buffer_size;
+ char* ptr;
+
+ aligned_offset = offset & ~(4096 - 1);
+ aligned_diff = offset - aligned_offset;
+ buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+ if (buffer_size > SIZE_MAX) return -E2BIG;
+ char* data =
+ reinterpret_cast<char*>(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+ if (data == MAP_FAILED) {
+ return -errno;
+ }
+ ptr = data + aligned_diff;
+#else
+ off64_t pos;
+ char* data = reinterpret_cast<char*>(malloc(len));
+ if (!data) {
+ return -errno;
+ }
+ pos = lseek64(fd, offset, SEEK_SET);
+ if (pos < 0) {
+ free(data);
+ return -errno;
+ }
+ ret = read_all(fd, data, len);
+ if (ret < 0) {
+ free(data);
+ return ret;
+ }
+ ptr = data;
+#endif
+
+ ret = out->sparse_ops->write_data_chunk(out, len, ptr);
- return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
+#ifndef _WIN32
+ munmap(data, buffer_size);
+#else
+ free(data);
+#endif
+
+ return ret;
}
/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset) {
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
int ret;
int file_fd = open(file, O_RDONLY | O_BINARY);
@@ -672,6 +713,6 @@ int write_file_chunk(struct output_file* out, uint64_t len, const char* file, in
return ret;
}
-int write_skip_chunk(struct output_file* out, uint64_t len) {
+int write_skip_chunk(struct output_file* out, int64_t len) {
return out->sparse_ops->write_skip_chunk(out, len);
}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index ecbcdf308..278430b6f 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -30,11 +30,11 @@ struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t
struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
unsigned int block_size, int64_t len, int gz,
int sparse, int chunks, int crc);
-int write_data_chunk(struct output_file* out, uint64_t len, void* data);
-int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val);
-int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset);
-int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset);
-int write_skip_chunk(struct output_file* out, uint64_t len);
+int write_data_chunk(struct output_file* out, unsigned int len, void* data);
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
void output_file_close(struct output_file* out);
int read_all(int fd, void* buf, size_t len);
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 396e7eb0c..8622b4c39 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -50,21 +50,21 @@ void sparse_file_destroy(struct sparse_file* s) {
free(s);
}
-int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block) {
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
return backed_block_add_data(s->backed_block_list, data, len, block);
}
-int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
unsigned int block) {
return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
}
int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
- uint64_t len, unsigned int block) {
+ unsigned int len, unsigned int block) {
return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
}
-int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
unsigned int block) {
return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
}
diff --git a/libstats/OWNERS b/libstats/OWNERS
index d39167947..7855774a7 100644
--- a/libstats/OWNERS
+++ b/libstats/OWNERS
@@ -1,7 +1,6 @@
-jeffreyhuang@google.com
-jtnguyen@google.com
+joeo@google.com
muhammadq@google.com
-sharaienko@google.com
+ruchirr@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
diff --git a/libstats/pull_lazy/Android.bp b/libstats/pull_lazy/Android.bp
deleted file mode 100644
index 65dce26a0..000000000
--- a/libstats/pull_lazy/Android.bp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Lazy loading version of libstatspull that can be used by code
-// that is running before the statsd APEX is mounted and
-// libstatspull.so is available.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
- name: "libstatspull_lazy",
- header_libs: [
- "libstatspull_headers",
- "libstatssocket_headers",
- ],
- export_header_lib_headers: [
- "libstatspull_headers",
- ],
- apex_available: ["//apex_available:platform"],
- srcs: ["libstatspull_lazy.cpp"],
-}
-
-cc_test {
- name: "libstatspull_lazy_test",
- srcs: [
- "tests/libstatspull_lazy_test.cpp",
- ],
- static_libs: [
- "libstatspull_lazy",
- "libstatssocket_lazy",
- ],
- shared_libs: ["liblog"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- test_suites: ["device-tests", "mts-statsd"],
- test_config: "libstatspull_lazy_test.xml",
- // TODO(b/153588990): Remove when the build system properly separates.
- // 32bit and 64bit architectures.
- compile_multilib: "both",
- multilib: {
- lib64: {
- suffix: "64",
- },
- lib32: {
- suffix: "32",
- },
- },
-}
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
deleted file mode 100644
index 89b8c2a1f..000000000
--- a/libstats/pull_lazy/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "libstatspull_lazy_test"
- }
- ]
-} \ No newline at end of file
diff --git a/libstats/pull_lazy/libstatspull_lazy.cpp b/libstats/pull_lazy/libstatspull_lazy.cpp
deleted file mode 100644
index b11fceec1..000000000
--- a/libstats/pull_lazy/libstatspull_lazy.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "libstatspull_lazy.h"
-
-#include <mutex>
-
-#include <dlfcn.h>
-#include <stdatomic.h>
-
-#include "log/log.h"
-
-#include "stats_pull_atom_callback.h"
-
-// This file provides a lazy interface to libstatspull.so to address early boot dependencies.
-// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and
-// libstatspull.so is in the statsd APEX.
-
-// Method pointers to libstatspull methods are held in an array which simplifies checking
-// all pointers are initialized.
-enum MethodIndex {
- // PullAtomMetadata APIs in stats_pull_atom_callback.h.
- k_AStatsManager_PullAtomMetadata_obtain,
- k_AStatsManager_PullAtomMetadata_release,
- k_AStatsManager_PullAtomMetadata_setCoolDownMillis,
- k_AStatsManager_PullAtomMetadata_getCoolDownMillis,
- k_AStatsManager_PullAtomMetadata_setTimeoutMillis,
- k_AStatsManager_PullAtomMetadata_getTimeoutMillis,
- k_AStatsManager_PullAtomMetadata_setAdditiveFields,
- k_AStatsManager_PullAtomMetadata_getNumAdditiveFields,
- k_AStatsManager_PullAtomMetadata_getAdditiveFields,
-
- // AStatsEventList APIs in stats_pull_atom_callback.h
- k_AStatsEventList_addStatsEvent,
-
- // PullAtomCallback APIs in stats_pull_atom_callback.h
- k_AStatsManager_setPullAtomCallback,
- k_AStatsManager_clearPullAtomCallback,
-
- // Marker for count of methods
- k_MethodCount
-};
-
-// Table of methods pointers in libstatspull APIs.
-static void* g_Methods[k_MethodCount];
-
-//
-// Libstatspull lazy loading.
-//
-
-static atomic_bool gPreventLibstatspullLoading = false; // Allows tests to block loading.
-
-void PreventLibstatspullLazyLoadingForTests() {
- gPreventLibstatspullLoading.store(true);
-}
-
-static void* LoadLibstatspull(int dlopen_flags) {
- if (gPreventLibstatspullLoading.load()) {
- return nullptr;
- }
- return dlopen("libstatspull.so", dlopen_flags);
-}
-
-//
-// Initialization and symbol binding.
-
-static void BindSymbol(void* handle, const char* name, enum MethodIndex index) {
- void* symbol = dlsym(handle, name);
- LOG_ALWAYS_FATAL_IF(symbol == nullptr, "Failed to find symbol '%s' in libstatspull.so: %s",
- name, dlerror());
- g_Methods[index] = symbol;
-}
-
-static void InitializeOnce() {
- void* handle = LoadLibstatspull(RTLD_NOW);
- LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load libstatspull.so: %s", dlerror());
-
-#undef BIND_SYMBOL
-#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name);
- // PullAtomMetadata APIs in stats_pull_atom_callback.h.
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_obtain);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_release);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_setCoolDownMillis);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_getCoolDownMillis);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_setTimeoutMillis);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_getTimeoutMillis);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_setAdditiveFields);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_getNumAdditiveFields);
- BIND_SYMBOL(AStatsManager_PullAtomMetadata_getAdditiveFields);
-
- // AStatsEventList APIs in stats_pull_atom_callback.h
- BIND_SYMBOL(AStatsEventList_addStatsEvent);
-
- // PullAtomCallback APIs in stats_pull_atom_callback.h
- BIND_SYMBOL(AStatsManager_setPullAtomCallback);
- BIND_SYMBOL(AStatsManager_clearPullAtomCallback);
-
-#undef BIND_SYMBOL
-
- // Check every symbol is bound.
- for (int i = 0; i < k_MethodCount; ++i) {
- LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr,
- "Uninitialized method in libstatspull_lazy at index: %d", i);
- }
-}
-
-static void EnsureInitialized() {
- static std::once_flag initialize_flag;
- std::call_once(initialize_flag, InitializeOnce);
-}
-
-#define INVOKE_METHOD(name, args...) \
- do { \
- EnsureInitialized(); \
- void* method = g_Methods[k_##name]; \
- return reinterpret_cast<decltype(&name)>(method)(args); \
- } while (0)
-
-//
-// Forwarding for methods in stats_pull_atom_callback.h.
-//
-
-AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_obtain);
-}
-
-void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_release, metadata);
-}
-
-void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
- int64_t cool_down_millis) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_setCoolDownMillis, metadata, cool_down_millis);
-}
-
-int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_getCoolDownMillis, metadata);
-}
-
-void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
- int64_t timeout_millis) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_setTimeoutMillis, metadata, timeout_millis);
-}
-
-int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_getTimeoutMillis, metadata);
-}
-
-void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
- int32_t* additive_fields,
- int32_t num_fields) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_setAdditiveFields, metadata, additive_fields,
- num_fields);
-}
-
-int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
- AStatsManager_PullAtomMetadata* metadata) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_getNumAdditiveFields, metadata);
-}
-
-void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
- int32_t* fields) {
- INVOKE_METHOD(AStatsManager_PullAtomMetadata_getAdditiveFields, metadata, fields);
-}
-
-AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
- INVOKE_METHOD(AStatsEventList_addStatsEvent, pull_data);
-}
-
-void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
- AStatsManager_PullAtomCallback callback, void* cookie) {
- INVOKE_METHOD(AStatsManager_setPullAtomCallback, atom_tag, metadata, callback, cookie);
-}
-
-void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
- INVOKE_METHOD(AStatsManager_clearPullAtomCallback, atom_tag);
-}
diff --git a/libstats/pull_lazy/libstatspull_lazy.h b/libstats/pull_lazy/libstatspull_lazy.h
deleted file mode 100644
index 2edddc770..000000000
--- a/libstats/pull_lazy/libstatspull_lazy.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-extern "C" void PreventLibstatspullLazyLoadingForTests(); \ No newline at end of file
diff --git a/libstats/pull_lazy/libstatspull_lazy_test.xml b/libstats/pull_lazy/libstatspull_lazy_test.xml
deleted file mode 100644
index 1b619af1e..000000000
--- a/libstats/pull_lazy/libstatspull_lazy_test.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs libstatspull_lazy_test.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
- <option name="test-suite-tag" value="mts" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="libstatspull_lazy_test->/data/local/tmp/libstatspull_lazy_test" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="libstatspull_lazy_test" />
- </test>
-
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
- </object>
-</configuration> \ No newline at end of file
diff --git a/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp b/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp
deleted file mode 100644
index 41f82d0b5..000000000
--- a/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "../libstatspull_lazy.h"
-
-#include <gtest/gtest.h>
-
-#include "stats_pull_atom_callback.h"
-//#include "stats_event.h"
-
-// The tests here are just for the case when libstatspull.so cannot be loaded by
-// libstatspull_lazy.
-class LibstatspullLazyTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- ::testing::Test::SetUp();
- PreventLibstatspullLazyLoadingForTests();
- }
-};
-
-static const char* kLoadFailed = "Failed to load libstatspull.so";
-
-TEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomMetadata) {
- AStatsManager_PullAtomMetadata* metadata = NULL;
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_obtain(), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_release(metadata), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 0), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 0), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, NULL, 0), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), kLoadFailed);
- EXPECT_DEATH(AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, NULL), kLoadFailed);
-}
-
-TEST_F(LibstatspullLazyTest, NoLibstatspullForAStatsEventList) {
- AStatsEventList* event_list = NULL;
- EXPECT_DEATH(AStatsEventList_addStatsEvent(event_list), kLoadFailed);
-}
-
-TEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomCallback) {
- AStatsManager_PullAtomCallback callback = NULL;
- EXPECT_DEATH(AStatsManager_setPullAtomCallback(0, NULL, callback, NULL), kLoadFailed);
- EXPECT_DEATH(AStatsManager_clearPullAtomCallback(0), kLoadFailed);
-} \ No newline at end of file
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
deleted file mode 100644
index 2a89e296e..000000000
--- a/libstats/pull_rust/Android.bp
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_bindgen {
- name: "libstatspull_bindgen",
- wrapper_src: "statslog.h",
- crate_name: "statspull_bindgen",
- source_stem: "bindings",
- bindgen_flags: [
- "--size_t-is-usize",
- "--allowlist-function=AStatsEventList_addStatsEvent",
- "--allowlist-function=AStatsEvent_.*",
- "--allowlist-function=AStatsManager_.*",
- "--allowlist-var=AStatsManager_.*",
- ],
- target: {
- android: {
- shared_libs: [
- "libstatspull",
- "libstatssocket",
- ],
- },
- host: {
- static_libs: [
- "libstatspull",
- "libstatssocket",
- ],
- },
- },
-}
-
-rust_library {
- name: "libstatspull_rust",
- crate_name: "statspull_rust",
- srcs: ["stats_pull.rs"],
- rustlibs: [
- "liblazy_static",
- "liblog_rust",
- "libstatslog_rust_header",
- "libstatspull_bindgen",
- ],
-}
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
deleted file mode 100644
index 174125e9a..000000000
--- a/libstats/pull_rust/stats_pull.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! A Rust interface for the StatsD pull API.
-
-use lazy_static::lazy_static;
-use statslog_rust_header::{Atoms, Stat, StatsError};
-use statspull_bindgen::*;
-use std::collections::HashMap;
-use std::convert::TryInto;
-use std::os::raw::c_void;
-use std::sync::Mutex;
-
-/// The return value of callbacks.
-pub type StatsPullResult = Vec<Box<dyn Stat>>;
-
-/// A wrapper for AStatsManager_PullAtomMetadata.
-/// It calls AStatsManager_PullAtomMetadata_release on drop.
-pub struct Metadata {
- metadata: *mut AStatsManager_PullAtomMetadata,
-}
-
-impl Metadata {
- /// Calls AStatsManager_PullAtomMetadata_obtain.
- pub fn new() -> Self {
- // Safety: We panic if the memory allocation fails.
- let metadata = unsafe { AStatsManager_PullAtomMetadata_obtain() };
- if metadata.is_null() {
- panic!("Cannot obtain pull atom metadata.");
- } else {
- Metadata { metadata }
- }
- }
-
- /// Calls AStatsManager_PullAtomMetadata_setCoolDownMillis.
- pub fn set_cooldown_millis(&mut self, cooldown_millis: i64) {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- unsafe { AStatsManager_PullAtomMetadata_setCoolDownMillis(self.metadata, cooldown_millis) }
- }
-
- /// Calls AStatsManager_PullAtomMetadata_getCoolDownMillis.
- pub fn get_cooldown_millis(&self) -> i64 {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- unsafe { AStatsManager_PullAtomMetadata_getCoolDownMillis(self.metadata) }
- }
-
- /// Calls AStatsManager_PullAtomMetadata_setTimeoutMillis.
- pub fn set_timeout_millis(&mut self, timeout_millis: i64) {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- unsafe { AStatsManager_PullAtomMetadata_setTimeoutMillis(self.metadata, timeout_millis) }
- }
-
- /// Calls AStatsManager_PullAtomMetadata_getTimeoutMillis.
- pub fn get_timeout_millis(&self) -> i64 {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- unsafe { AStatsManager_PullAtomMetadata_getTimeoutMillis(self.metadata) }
- }
-
- /// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.
- pub fn set_additive_fields(&mut self, additive_fields: &mut Vec<i32>) {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- unsafe {
- AStatsManager_PullAtomMetadata_setAdditiveFields(
- self.metadata,
- additive_fields.as_mut_ptr(),
- additive_fields.len().try_into().expect("Cannot convert length to i32"),
- )
- }
- }
-
- /// Calls AStatsManager_PullAtomMetadata_getAdditiveFields.
- pub fn get_additive_fields(&self) -> Vec<i32> {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- // We call getNumAdditiveFields to ensure we pass getAdditiveFields a large enough array.
- unsafe {
- let num_fields = AStatsManager_PullAtomMetadata_getNumAdditiveFields(self.metadata)
- .try_into()
- .expect("Cannot convert num additive fields to usize");
- let mut fields = vec![0; num_fields];
- AStatsManager_PullAtomMetadata_getAdditiveFields(self.metadata, fields.as_mut_ptr());
- fields
- }
- }
-}
-
-impl Drop for Metadata {
- fn drop(&mut self) {
- // Safety: Metadata::new ensures that self.metadata is a valid object.
- unsafe { AStatsManager_PullAtomMetadata_release(self.metadata) }
- }
-}
-
-impl Default for Metadata {
- fn default() -> Self {
- Self::new()
- }
-}
-
-lazy_static! {
- static ref COOKIES: Mutex<HashMap<i32, fn() -> StatsPullResult>> = Mutex::new(HashMap::new());
-}
-
-// Safety: We store our callbacks in the global so they are valid.
-unsafe extern "C" fn callback_wrapper(
- atom_tag: i32,
- data: *mut AStatsEventList,
- _cookie: *mut c_void,
-) -> AStatsManager_PullAtomCallbackReturn {
- if !data.is_null() {
- let map = COOKIES.lock().unwrap();
- let cb = map.get(&atom_tag);
- match cb {
- None => log::error!("No callback found for {}", atom_tag),
- Some(cb) => {
- let stats = cb();
- let result = stats
- .iter()
- .map(|stat| stat.add_astats_event(&mut *data))
- .collect::<Result<Vec<()>, StatsError>>();
- match result {
- Ok(_) => {
- return AStatsManager_PULL_SUCCESS as AStatsManager_PullAtomCallbackReturn
- }
- _ => log::error!("Error adding astats events: {:?}", result),
- }
- }
- }
- }
- AStatsManager_PULL_SKIP as AStatsManager_PullAtomCallbackReturn
-}
-
-/// Rust wrapper for AStatsManager_setPullAtomCallback.
-pub fn set_pull_atom_callback(
- atom: Atoms,
- metadata: Option<&Metadata>,
- callback: fn() -> StatsPullResult,
-) {
- COOKIES.lock().unwrap().insert(atom as i32, callback);
- let metadata_raw = match metadata {
- Some(m) => m.metadata,
- None => std::ptr::null_mut(),
- };
- // Safety: We pass a valid function as the callback.
- unsafe {
- AStatsManager_setPullAtomCallback(
- atom as i32,
- metadata_raw,
- Some(callback_wrapper),
- std::ptr::null_mut(),
- );
- }
-}
-
-/// Rust wrapper for AStatsManager_clearPullAtomCallback.
-pub fn clear_pull_atom_callback(atom: Atoms) {
- COOKIES.lock().unwrap().remove(&(atom as i32));
- // Safety: No memory allocations.
- unsafe { AStatsManager_clearPullAtomCallback(atom as i32) }
-}
diff --git a/libstats/pull_rust/statslog.h b/libstats/pull_rust/statslog.h
deleted file mode 100644
index 983fb7b72..000000000
--- a/libstats/pull_rust/statslog.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-#include "stats_pull_atom_callback.h"
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 819066eb4..a63a5b63b 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -19,10 +19,6 @@
// protocols. This library should only be used by DNS resolver or other
// native modules on Q that log pushed atoms to statsd.
// =========================================================================
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "libstatspush_compat_defaults",
srcs: [
@@ -36,10 +32,7 @@ cc_defaults {
"-DWRITE_TO_STATSD=1",
"-DWRITE_TO_LOGD=0",
],
- header_libs: [
- "libcutils_headers",
- "libstatssocket_headers",
- ],
+ header_libs: ["libstatssocket_headers"],
static_libs: [
"libbase",
],
@@ -55,7 +48,7 @@ cc_library {
export_header_lib_headers: [
"libstatssocket_headers",
],
- header_libs: ["libgtest_prod_headers"],
+ static_libs: ["libgtest_prod"],
apex_available: ["com.android.resolv"],
min_sdk_version: "29",
}
@@ -69,3 +62,4 @@ cc_test {
],
static_libs: ["libgmock"],
}
+
diff --git a/libstats/socket_lazy/Android.bp b/libstats/socket_lazy/Android.bp
deleted file mode 100644
index b2cd7b26b..000000000
--- a/libstats/socket_lazy/Android.bp
+++ /dev/null
@@ -1,44 +0,0 @@
-// Lazy loading version of libstatssocket that can be used by code
-// that is running before the statsd APEX is mounted and
-// libstatssocket.so is available.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
- name: "libstatssocket_lazy",
- header_libs: [
- "libstatssocket_headers",
- ],
- export_header_lib_headers: [
- "libstatssocket_headers",
- ],
- apex_available: ["//apex_available:platform"],
- srcs: ["libstatssocket_lazy.cpp"],
-}
-
-cc_test {
- name: "libstatssocket_lazy_test",
- srcs: [
- "tests/libstatssocket_lazy_test.cpp",
- ],
- static_libs: ["libstatssocket_lazy"],
- shared_libs: ["liblog"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- test_suites: ["device-tests", "mts-statsd"],
- test_config: "libstatssocket_lazy_test.xml",
- // TODO(b/153588990): Remove when the build system properly separates.
- // 32bit and 64bit architectures.
- compile_multilib: "both",
- multilib: {
- lib64: {
- suffix: "64",
- },
- lib32: {
- suffix: "32",
- },
- },
-}
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
deleted file mode 100644
index 13afc0087..000000000
--- a/libstats/socket_lazy/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "libstatssocket_lazy_test"
- }
- ]
-} \ No newline at end of file
diff --git a/libstats/socket_lazy/libstatssocket_lazy.cpp b/libstats/socket_lazy/libstatssocket_lazy.cpp
deleted file mode 100644
index dd93eebf0..000000000
--- a/libstats/socket_lazy/libstatssocket_lazy.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "libstatssocket_lazy.h"
-
-#include <mutex>
-
-#include <dlfcn.h>
-#include <stdatomic.h>
-
-#include "log/log.h"
-
-#include "stats_event.h"
-#include "stats_socket.h"
-
-// This file provides a lazy interface to libstatssocket.so to address early boot dependencies.
-// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and
-// libstatssocket.so is in the statsd APEX.
-
-// Method pointers to libstatssocket methods are held in an array which simplifies checking
-// all pointers are initialized.
-enum MethodIndex {
- // Stats Event APIs in stats_event.h.
- k_AStatsEvent_obtain,
- k_AStatsEvent_build,
- k_AStatsEvent_write,
- k_AStatsEvent_release,
- k_AStatsEvent_setAtomId,
- k_AStatsEvent_writeInt32,
- k_AStatsEvent_writeInt64,
- k_AStatsEvent_writeFloat,
- k_AStatsEvent_writeBool,
- k_AStatsEvent_writeByteArray,
- k_AStatsEvent_writeString,
- k_AStatsEvent_writeAttributionChain,
- k_AStatsEvent_addBoolAnnotation,
- k_AStatsEvent_addInt32Annotation,
-
- // Stats Socket APIs in stats_socket.h.
- k_AStatsSocket_close,
-
- // Marker for count of methods
- k_MethodCount
-};
-
-// Table of methods pointers in libstatssocket APIs.
-static void* g_Methods[k_MethodCount];
-
-//
-// Libstatssocket lazy loading.
-//
-
-static atomic_bool gPreventLibstatssocketLoading = false; // Allows tests to block loading.
-
-void PreventLibstatssocketLazyLoadingForTests() {
- gPreventLibstatssocketLoading.store(true);
-}
-
-static void* LoadLibstatssocket(int dlopen_flags) {
- if (gPreventLibstatssocketLoading.load()) {
- return nullptr;
- }
- return dlopen("libstatssocket.so", dlopen_flags);
-}
-
-//
-// Initialization and symbol binding.
-
-static void BindSymbol(void* handle, const char* name, enum MethodIndex index) {
- void* symbol = dlsym(handle, name);
- LOG_ALWAYS_FATAL_IF(symbol == nullptr, "Failed to find symbol '%s' in libstatssocket.so: %s",
- name, dlerror());
- g_Methods[index] = symbol;
-}
-
-static void InitializeOnce() {
- void* handle = LoadLibstatssocket(RTLD_NOW);
- LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load libstatssocket.so: %s", dlerror());
-
-#undef BIND_SYMBOL
-#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name);
- // Methods in stats_event.h.
- BIND_SYMBOL(AStatsEvent_obtain);
- BIND_SYMBOL(AStatsEvent_build);
- BIND_SYMBOL(AStatsEvent_write);
- BIND_SYMBOL(AStatsEvent_release);
- BIND_SYMBOL(AStatsEvent_setAtomId);
- BIND_SYMBOL(AStatsEvent_writeInt32);
- BIND_SYMBOL(AStatsEvent_writeInt64);
- BIND_SYMBOL(AStatsEvent_writeFloat);
- BIND_SYMBOL(AStatsEvent_writeBool);
- BIND_SYMBOL(AStatsEvent_writeByteArray);
- BIND_SYMBOL(AStatsEvent_writeString);
- BIND_SYMBOL(AStatsEvent_writeAttributionChain);
- BIND_SYMBOL(AStatsEvent_addBoolAnnotation);
- BIND_SYMBOL(AStatsEvent_addInt32Annotation);
-
- // Methods in stats_socket.h.
- BIND_SYMBOL(AStatsSocket_close);
-#undef BIND_SYMBOL
-
- // Check every symbol is bound.
- for (int i = 0; i < k_MethodCount; ++i) {
- LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr,
- "Uninitialized method in libstatssocket_lazy at index: %d", i);
- }
-}
-
-static void EnsureInitialized() {
- static std::once_flag initialize_flag;
- std::call_once(initialize_flag, InitializeOnce);
-}
-
-#define INVOKE_METHOD(name, args...) \
- do { \
- EnsureInitialized(); \
- void* method = g_Methods[k_##name]; \
- return reinterpret_cast<decltype(&name)>(method)(args); \
- } while (0)
-
-//
-// Forwarding for methods in stats_event.h.
-//
-
-AStatsEvent* AStatsEvent_obtain() {
- INVOKE_METHOD(AStatsEvent_obtain);
-}
-
-void AStatsEvent_build(AStatsEvent* event) {
- INVOKE_METHOD(AStatsEvent_build, event);
-}
-
-int AStatsEvent_write(AStatsEvent* event) {
- INVOKE_METHOD(AStatsEvent_write, event);
-}
-
-void AStatsEvent_release(AStatsEvent* event) {
- INVOKE_METHOD(AStatsEvent_release, event);
-}
-
-void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
- INVOKE_METHOD(AStatsEvent_setAtomId, event, atomId);
-}
-
-void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {
- INVOKE_METHOD(AStatsEvent_writeInt32, event, value);
-}
-
-void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {
- INVOKE_METHOD(AStatsEvent_writeInt64, event, value);
-}
-
-void AStatsEvent_writeFloat(AStatsEvent* event, float value) {
- INVOKE_METHOD(AStatsEvent_writeFloat, event, value);
-}
-
-void AStatsEvent_writeBool(AStatsEvent* event, bool value) {
- INVOKE_METHOD(AStatsEvent_writeBool, event, value);
-}
-
-void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
- INVOKE_METHOD(AStatsEvent_writeByteArray, event, buf, numBytes);
-}
-
-void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
- INVOKE_METHOD(AStatsEvent_writeString, event, value);
-}
-
-void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
- const char* const* tags, uint8_t numNodes) {
- INVOKE_METHOD(AStatsEvent_writeAttributionChain, event, uids, tags, numNodes);
-}
-
-void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {
- INVOKE_METHOD(AStatsEvent_addBoolAnnotation, event, annotationId, value);
-}
-
-void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {
- INVOKE_METHOD(AStatsEvent_addInt32Annotation, event, annotationId, value);
-}
-
-//
-// Forwarding for methods in stats_socket.h.
-//
-
-void AStatsSocket_close() {
- INVOKE_METHOD(AStatsSocket_close);
-} \ No newline at end of file
diff --git a/libstats/socket_lazy/libstatssocket_lazy.h b/libstats/socket_lazy/libstatssocket_lazy.h
deleted file mode 100644
index 3ff87cbfa..000000000
--- a/libstats/socket_lazy/libstatssocket_lazy.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-extern "C" void PreventLibstatssocketLazyLoadingForTests(); \ No newline at end of file
diff --git a/libstats/socket_lazy/libstatssocket_lazy_test.xml b/libstats/socket_lazy/libstatssocket_lazy_test.xml
deleted file mode 100644
index ca6339b13..000000000
--- a/libstats/socket_lazy/libstatssocket_lazy_test.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs libstatssocket_lazy_test.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
- <option name="test-suite-tag" value="mts" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="libstatssocket_lazy_test->/data/local/tmp/libstatssocket_lazy_test" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="libstatssocket_lazy_test" />
- </test>
-
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
- </object>
-</configuration> \ No newline at end of file
diff --git a/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp b/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
deleted file mode 100644
index fe1359860..000000000
--- a/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "../libstatssocket_lazy.h"
-
-#include <gtest/gtest.h>
-
-#include "stats_event.h"
-#include "stats_socket.h"
-
-// The tests here are just for the case when libstatssocket.so cannot be loaded by
-// libstatssocket_lazy.
-class LibstatssocketLazyTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- ::testing::Test::SetUp();
- PreventLibstatssocketLazyLoadingForTests();
- }
-};
-
-static const char* kLoadFailed = "Failed to load libstatssocket.so";
-
-TEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsEvent) {
- AStatsEvent* event = NULL;
- EXPECT_DEATH(AStatsEvent_obtain(), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_build(event), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_write(event), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_release(event), kLoadFailed);
-
- EXPECT_DEATH(AStatsEvent_setAtomId(event, 0), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeInt32(event, 0), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeInt64(event, 0), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeFloat(event, 0), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeBool(event, false), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeByteArray(event, NULL, 0), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeString(event, NULL), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_writeAttributionChain(event, NULL, NULL, 0), kLoadFailed);
-
- EXPECT_DEATH(AStatsEvent_addBoolAnnotation(event, 0, false), kLoadFailed);
- EXPECT_DEATH(AStatsEvent_addInt32Annotation(event, 0, 0), kLoadFailed);
-}
-
-TEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsSocket) {
- EXPECT_DEATH(AStatsSocket_close(), kLoadFailed);
-} \ No newline at end of file
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 671de4d98..c5f1f5e0a 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -1,9 +1,5 @@
// Copyright 2012 The Android Open Source Project
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libsuspend",
srcs: [
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 99c88cf21..bad623054 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -1,20 +1,3 @@
-package {
- default_applicable_licenses: ["system_core_libsync_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_core_libsync_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
ndk_headers {
name: "libsync_headers",
from: "include/ndk",
@@ -42,9 +25,6 @@ cc_library {
recovery_available: true,
native_bridge_supported: true,
defaults: ["libsync_defaults"],
- llndk: {
- symbol_file: "libsync.map.txt",
- },
stubs: {
symbol_file: "libsync.map.txt",
versions: [
@@ -53,6 +33,12 @@ cc_library {
},
}
+llndk_library {
+ name: "libsync",
+ symbol_file: "libsync.map.txt",
+ export_include_dirs: ["include"],
+}
+
cc_test {
name: "sync-unit-tests",
shared_libs: ["libsync"],
diff --git a/libsync/OWNERS b/libsync/OWNERS
index e75b15b8f..dc61733a7 100644
--- a/libsync/OWNERS
+++ b/libsync/OWNERS
@@ -1,3 +1,3 @@
-chrisforbes@google.com
-hridya@google.com
+ghackmann@google.com
jessehall@google.com
+marissaw@google.com
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
index 38ccb686c..2a59e35bb 100644
--- a/libsync/include/ndk/sync.h
+++ b/libsync/include/ndk/sync.h
@@ -33,6 +33,8 @@
__BEGIN_DECLS
+#if __ANDROID_API__ >= 26
+
/* Fences indicate the status of an asynchronous task. They are initially
* in unsignaled state (0), and make a one-time transition to either signaled
* (1) or error (< 0) state. A sync file is a collection of one or more fences;
@@ -99,6 +101,8 @@ static inline struct sync_fence_info* sync_get_fence_info(const struct sync_file
*/
void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
+#endif /* __ANDROID_API__ >= 26 */
+
__END_DECLS
#endif /* ANDROID_SYNC_H */
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index dacc8cad3..db616697c 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,13 +1,7 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libsystem_headers",
vendor_available: true,
- product_available: true,
recovery_available: true,
- vendor_ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
diff --git a/libsystem/OWNERS b/libsystem/OWNERS
index 9bda04c4d..fdea80467 100644
--- a/libsystem/OWNERS
+++ b/libsystem/OWNERS
@@ -1,6 +1,9 @@
# graphics/composer
adyabr@google.com
lpy@google.com
+marissaw@google.com
+stoza@google.com
+vhau@google.com
# camera
etalvala@google.com
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 5f472b2ab..3b98babb6 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libsysutils",
vendor_available: true,
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 3b6cfd830..9c1621bc8 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -185,6 +185,7 @@ bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
+ // Sanity check.
int type = nh->nlmsg_type;
if (type != RTM_NEWADDR && type != RTM_DELADDR) {
SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
@@ -348,6 +349,7 @@ bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {
uint8_t type = nh->nlmsg_type;
const char *msgname = rtMessageName(type);
+ // Sanity check.
if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
return false;
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index e90afcd18..fe2f3d668 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -201,31 +201,50 @@ int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
return 0;
}
+ int ret = 0;
+ int e = 0; // SLOGW and sigaction are not inert regarding errno
int current = 0;
+ struct sigaction new_action, old_action;
+ memset(&new_action, 0, sizeof(new_action));
+ new_action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &new_action, &old_action);
+
for (;;) {
- ssize_t rc = TEMP_FAILURE_RETRY(writev(mSocket, iov + current, iovcnt - current));
+ ssize_t rc = TEMP_FAILURE_RETRY(
+ writev(mSocket, iov + current, iovcnt - current));
+
+ if (rc > 0) {
+ size_t written = rc;
+ while ((current < iovcnt) && (written >= iov[current].iov_len)) {
+ written -= iov[current].iov_len;
+ current++;
+ }
+ if (current == iovcnt) {
+ break;
+ }
+ iov[current].iov_base = (char *)iov[current].iov_base + written;
+ iov[current].iov_len -= written;
+ continue;
+ }
if (rc == 0) {
- errno = EIO;
+ e = EIO;
SLOGW("0 length write :(");
- return -1;
- } else if (rc < 0) {
- SLOGW("write error (%s)", strerror(errno));
- return -1;
+ } else {
+ e = errno;
+ SLOGW("write error (%s)", strerror(e));
}
+ ret = -1;
+ break;
+ }
- size_t written = rc;
- while (current < iovcnt && written >= iov[current].iov_len) {
- written -= iov[current].iov_len;
- current++;
- }
- if (current == iovcnt) {
- return 0;
- }
- iov[current].iov_base = (char*)iov[current].iov_base + written;
- iov[current].iov_len -= written;
+ sigaction(SIGPIPE, &old_action, &new_action);
+
+ if (e != 0) {
+ errno = e;
}
+ return ret;
}
void SocketClient::incRef() {
diff --git a/libunwindstack/.clang-format b/libunwindstack/.clang-format
new file mode 120000
index 000000000..fd0645fdf
--- /dev/null
+++ b/libunwindstack/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2 \ No newline at end of file
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
new file mode 100644
index 000000000..9b974c237
--- /dev/null
+++ b/libunwindstack/Android.bp
@@ -0,0 +1,412 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libunwindstack_flags",
+
+ host_supported: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ },
+}
+
+cc_defaults {
+ name: "libunwindstack_defaults",
+ defaults: ["libunwindstack_flags"],
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "ArmExidx.cpp",
+ "DexFiles.cpp",
+ "DwarfCfa.cpp",
+ "DwarfEhFrameWithHdr.cpp",
+ "DwarfMemory.cpp",
+ "DwarfOp.cpp",
+ "DwarfSection.cpp",
+ "Elf.cpp",
+ "ElfInterface.cpp",
+ "ElfInterfaceArm.cpp",
+ "Global.cpp",
+ "JitDebug.cpp",
+ "Log.cpp",
+ "MapInfo.cpp",
+ "Maps.cpp",
+ "Memory.cpp",
+ "LocalUnwinder.cpp",
+ "Regs.cpp",
+ "RegsArm.cpp",
+ "RegsArm64.cpp",
+ "RegsX86.cpp",
+ "RegsX86_64.cpp",
+ "RegsMips.cpp",
+ "RegsMips64.cpp",
+ "Unwinder.cpp",
+ "Symbols.cpp",
+ ],
+
+ cflags: [
+ "-Wexit-time-destructors",
+ ],
+
+ target: {
+ // Always disable optimizations for host to make it easier to debug.
+ host: {
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+ },
+ },
+
+ arch: {
+ x86: {
+ srcs: ["AsmGetRegsX86.S"],
+ },
+ x86_64: {
+ srcs: ["AsmGetRegsX86_64.S"],
+ },
+ mips: {
+ srcs: ["AsmGetRegsMips.S"],
+ },
+ mips64: {
+ srcs: ["AsmGetRegsMips64.S"],
+ },
+ },
+
+ static_libs: [
+ "libprocinfo",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "liblzma",
+ ],
+}
+
+cc_library {
+ name: "libunwindstack",
+ vendor_available: true,
+ recovery_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ defaults: ["libunwindstack_defaults"],
+
+ srcs: ["DexFile.cpp"],
+ cflags: ["-DDEXFILE_SUPPORT"],
+ shared_libs: ["libdexfile_support"],
+
+ target: {
+ vendor: {
+ cflags: ["-UDEXFILE_SUPPORT"],
+ exclude_srcs: ["DexFile.cpp"],
+ exclude_shared_libs: ["libdexfile_support"],
+ },
+ recovery: {
+ cflags: ["-UDEXFILE_SUPPORT"],
+ exclude_srcs: ["DexFile.cpp"],
+ exclude_shared_libs: ["libdexfile_support"],
+ },
+ native_bridge: {
+ cflags: ["-UDEXFILE_SUPPORT"],
+ exclude_srcs: ["DexFile.cpp"],
+ exclude_shared_libs: ["libdexfile_support"],
+ },
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
+}
+
+// Static library without DEX support to avoid dependencies on the ART APEX.
+cc_library_static {
+ name: "libunwindstack_no_dex",
+ recovery_available: true,
+ defaults: ["libunwindstack_defaults"],
+
+ visibility: [
+ "//system/core/debuggerd",
+ "//system/core/init",
+ "//system/core/libbacktrace",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.runtime",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// Unit Tests
+//-------------------------------------------------------------------------
+cc_test_library {
+ name: "libunwindstack_local",
+ defaults: ["libunwindstack_flags"],
+ srcs: ["tests/TestLocal.cpp"],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+ relative_install_path: "libunwindstack_test",
+}
+
+cc_defaults {
+ name: "libunwindstack_testlib_flags",
+ defaults: ["libunwindstack_flags"],
+
+ srcs: [
+ "tests/ArmExidxDecodeTest.cpp",
+ "tests/ArmExidxExtractTest.cpp",
+ "tests/DexFileTest.cpp",
+ "tests/DexFilesTest.cpp",
+ "tests/DwarfCfaLogTest.cpp",
+ "tests/DwarfCfaTest.cpp",
+ "tests/DwarfDebugFrameTest.cpp",
+ "tests/DwarfEhFrameTest.cpp",
+ "tests/DwarfEhFrameWithHdrTest.cpp",
+ "tests/DwarfMemoryTest.cpp",
+ "tests/DwarfOpLogTest.cpp",
+ "tests/DwarfOpTest.cpp",
+ "tests/DwarfSectionTest.cpp",
+ "tests/DwarfSectionImplTest.cpp",
+ "tests/ElfCacheTest.cpp",
+ "tests/ElfFake.cpp",
+ "tests/ElfInterfaceArmTest.cpp",
+ "tests/ElfInterfaceTest.cpp",
+ "tests/ElfTest.cpp",
+ "tests/ElfTestUtils.cpp",
+ "tests/IsolatedSettings.cpp",
+ "tests/JitDebugTest.cpp",
+ "tests/LocalUpdatableMapsTest.cpp",
+ "tests/LogFake.cpp",
+ "tests/MapInfoCreateMemoryTest.cpp",
+ "tests/MapInfoGetBuildIDTest.cpp",
+ "tests/MapInfoGetElfTest.cpp",
+ "tests/MapInfoGetLoadBiasTest.cpp",
+ "tests/MapInfoTest.cpp",
+ "tests/MapsTest.cpp",
+ "tests/MemoryBufferTest.cpp",
+ "tests/MemoryCacheTest.cpp",
+ "tests/MemoryFake.cpp",
+ "tests/MemoryFileTest.cpp",
+ "tests/MemoryLocalTest.cpp",
+ "tests/MemoryOfflineBufferTest.cpp",
+ "tests/MemoryOfflineTest.cpp",
+ "tests/MemoryRangeTest.cpp",
+ "tests/MemoryRangesTest.cpp",
+ "tests/MemoryRemoteTest.cpp",
+ "tests/MemoryTest.cpp",
+ "tests/RegsInfoTest.cpp",
+ "tests/RegsIterateTest.cpp",
+ "tests/RegsStepIfSignalHandlerTest.cpp",
+ "tests/RegsTest.cpp",
+ "tests/SymbolsTest.cpp",
+ "tests/TestUtils.cpp",
+ "tests/UnwindOfflineTest.cpp",
+ "tests/UnwindTest.cpp",
+ "tests/UnwinderTest.cpp",
+ "tests/VerifyBionicTerminationTest.cpp",
+ ],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "liblzma",
+ "libunwindstack",
+ "libdexfile_support",
+ ],
+
+ static_libs: [
+ "libgmock",
+ ],
+
+ test_suites: ["device-tests"],
+ data: [
+ "tests/files/elf32.xz",
+ "tests/files/elf64.xz",
+ "tests/files/offline/art_quick_osr_stub_arm/*",
+ "tests/files/offline/bad_eh_frame_hdr_arm64/*",
+ "tests/files/offline/debug_frame_first_x86/*",
+ "tests/files/offline/debug_frame_load_bias_arm/*",
+ "tests/files/offline/eh_frame_bias_x86/*",
+ "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+ "tests/files/offline/empty_arm64/*",
+ "tests/files/offline/invalid_elf_offset_arm/*",
+ "tests/files/offline/jit_debug_arm/*",
+ "tests/files/offline/jit_debug_x86/*",
+ "tests/files/offline/jit_map_arm/*",
+ "tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/load_bias_different_section_bias_arm64/*",
+ "tests/files/offline/load_bias_ro_rx_x86_64/*",
+ "tests/files/offline/offset_arm/*",
+ "tests/files/offline/shared_lib_in_apk_arm64/*",
+ "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
+ "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
+ "tests/files/offline/signal_load_bias_arm/*",
+ "tests/files/offline/straddle_arm/*",
+ "tests/files/offline/straddle_arm64/*",
+ ],
+}
+
+cc_test {
+ name: "libunwindstack_test",
+ defaults: ["libunwindstack_testlib_flags"],
+ isolated: true,
+
+ srcs: [
+ "tests/LocalUnwinderTest.cpp",
+ ],
+ required: [
+ "libunwindstack_local",
+ ],
+}
+
+// Skip LocalUnwinderTest until atest understands required properly.
+cc_test {
+ name: "libunwindstack_unit_test",
+ defaults: ["libunwindstack_testlib_flags"],
+ isolated: true,
+}
+
+//-------------------------------------------------------------------------
+// Tools
+//-------------------------------------------------------------------------
+cc_defaults {
+ name: "libunwindstack_tools",
+ defaults: ["libunwindstack_flags"],
+
+ shared_libs: [
+ "libunwindstack",
+ "libbase",
+ "liblzma",
+ ],
+ target: {
+ // Always disable optimizations for host to make it easier to debug.
+ host: {
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+ },
+ },
+}
+
+cc_binary {
+ name: "unwind",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind.cpp",
+ ],
+}
+
+cc_binary {
+ name: "unwind_info",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind_info.cpp",
+ ],
+}
+
+cc_binary {
+ name: "unwind_symbols",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind_symbols.cpp",
+ ],
+}
+
+cc_binary {
+ name: "unwind_for_offline",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind_for_offline.cpp",
+ ],
+}
+
+cc_binary {
+ name: "unwind_reg_info",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind_reg_info.cpp",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// Benchmarks
+//-------------------------------------------------------------------------
+cc_benchmark {
+ name: "unwind_benchmarks",
+ host_supported: true,
+ defaults: ["libunwindstack_flags"],
+
+ // Disable optimizations so that all of the calls are not optimized away.
+ cflags: [
+ "-O0",
+ ],
+
+ srcs: [
+ "benchmarks/unwind_benchmarks.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libunwindstack",
+ ],
+}
+
+// Generates the elf data for use in the tests for .gnu_debugdata frames.
+// Once these files are generated, use the xz command to compress the data.
+cc_binary_host {
+ name: "gen_gnudebugdata",
+ defaults: ["libunwindstack_flags"],
+
+ srcs: [
+ "tests/GenGnuDebugdata.cpp",
+ ],
+}
diff --git a/libunwindstack/AndroidVersions.md b/libunwindstack/AndroidVersions.md
new file mode 100644
index 000000000..234f6391b
--- /dev/null
+++ b/libunwindstack/AndroidVersions.md
@@ -0,0 +1,116 @@
+# Unwinder Support Per Android Release
+This document describes the changes in the way the libunwindstack
+unwinder works on different Android versions. It does not describe
+every change in the code made between different versions, but is
+meant to allow an app developer to know what might be supported
+on different versions. It also describes the different way an unwind
+will display on different versions of Android.
+
+## Android P
+libunwindstack was first introduced in Android P.
+
+* Supports up to and including Dwarf 4 unwinding information.
+ See http://dwarfstd.org/ for Dwarf standards.
+* Supports Arm exidx unwinding.
+* Supports the gdb JIT unwinding interface, which is how ART creates unwinding
+ information for the JIT'd Java frames.
+* Supports special frames added to represent an ART Java interpreter frame.
+ ART has marked the dex pc using cfi information that the unwinder
+ understands and handles by adding a new frame in the stacktrace.
+
+## Note
+By default, lld creates two separate maps of the elf in memory, one read-only
+and one read/executable. The libunwindstack on P and the unwinder on older
+versions of Android will not unwind properly in this case. For apps that
+target Android P or older, make sure that `-Wl,--no-rosegment` is
+included in linker arguments when using lld.
+
+## Android Q
+* Fix bug (b/109824792) that handled load bias data incorrectly when
+ FDEs use pc relative addressing in the eh\_frame\_hdr.
+ Unfortunately, this wasn't fixed correctly in Q since it assumes
+ that the bias is coming from the program header for the executable
+ load. The real fix was to use the bias from the actual section data and
+ is not completely fixed until Android R. For apps targeting Android Q,
+ if it is being compiled with the llvm linker lld, it might be necessary
+ to add the linker option `-Wl,-zseparate-code` to avoid creating an elf
+ created this way.
+* Change the way the exidx section offset is found (b/110704153). Before
+ the p\_vaddr value from the program header minus the load bias was used
+ to find the start of the exidx data. Changed to use the p\_offset since
+ it doesn't require any load bias manipulations.
+* Fix bug handling of dwarf sections without any header (b/110235461).
+ Previously, the code assumed that FDEs are non-overlapping, and the FDEs
+ are always in sorted order from low pc to high pc. Thus the code would
+ read the entire set of CIEs/FDEs and then do a binary search to find
+ the appropriate FDE for a given pc. Now the code does a sequential read
+ and stops when it finds the FDE for a pc. It also understands the
+ overlapping FDEs, so find the first FDE that matches a pc. In practice,
+ elf files with this format only ever occurs if the file was generated
+ without an eh\_frame/eh\_frame\_hdr section and only a debug\_frame. The
+ other way this has been observed is when running simpleperf to unwind since
+ sometimes there is not enough information in the eh\_frame for all points
+ in the executable. On Android P, this would result in some incorrect
+ unwinds coming from simpleperf. Nearly all crashes from Android P should
+ be correct since the eh\_frame information was enough to do the unwind
+ properly.
+* Be permissive of badly formed elf files. Previously, any detected error
+ would result in unwinds stopping even if there is enough valid information
+ to do an unwind.
+ * The code now allows program header/section header offsets to point
+ to unreadable memory. As long as the code can find the unwind tables,
+ that is good enough.
+ * The code allows program headers/section headers to be missing.
+ * Allow a symbol table section header to point to invalid symbol table
+ values.
+* Support for the linker read-only segment option (b/109657296).
+ This is a feature of lld whereby there are two sections that
+ contain elf data. The first is read-only and contains the elf header data,
+ and the second is read-execute or execute only that
+ contains the executable code from the elf. Before this, the unwinder
+ always assumed that there was only a single read-execute section that
+ contained the elf header data and the executable code.
+* Build ID information for elf objects added. This will display the
+ NT\_GNU\_BUILD\_ID note found in elf files. This information can be used
+ to identify the exact version of a shared library to help get symbol
+ information when looking at a crash.
+* Add support for displaying the soname from an apk frame. Previously,
+ a frame map name would be only the apk, but now if the shared library
+ in the apk has set a soname, the map name will be `app.apk!libexample.so`
+ instead of only `app.apk`.
+* Minimal support for Dwarf 5. This merely treats a Dwarf 5 version
+ elf file as Dwarf 4. It does not support the new dwarf ops in Dwarf 5.
+ Since the new ops are not likely to be used very often, this allows
+ continuing to unwind even when encountering Dwarf 5 elf files.
+* Fix bug in pc handling of signal frames (b/130302288). In the previous
+ version, the pc would be wrong in the signal frame. The rest of the
+ unwind was correct, only the frame in the signal handler was incorrect
+ in Android P.
+* Detect when an elf file is not readable so that a message can be
+ displayed indicating that. This can happen when an app puts the shared
+ libraries in non-standard locations that are not readable due to
+ security restrictions (selinux rules).
+
+## Android R
+* Display the offsets for Java interpreter frames. If this frame came
+ from a non-zero offset map, no offset is printed. Previously, the
+ line would look like:
+
+ #17 pc 00500d7a GoogleCamera.apk (com.google.camera.AndroidPriorityThread.run+10)
+
+ to:
+
+ #17 pc 00500d7a GoogleCamera.apk (offset 0x11d0000) (com.google.camera.AndroidPriorityThread.run+10)
+* Fix bug where the load bias was set from the first PT\_LOAD program
+ header that has a zero p\_offset value. Now it is set from the first
+ executable PT\_LOAD program header. This has only ever been a problem
+ for host executables compiled for the x86\_64 architecture.
+* Switched to the libc++ demangler for function names. Previously, the
+ demangler used was not complete, so some less common demangled function
+ names would not be properly demangled or the function name would not be
+ demangled at all.
+* Fix bug in load bias handling. If the unwind information in the eh\_frame
+ or eh\_frame\_hdr does not have the same bias as the executable section,
+ and uses pc relative FDEs, the unwind will be incorrect. This tends
+ to truncate unwinds since the unwinder could not find the correct unwind
+ information for a given pc.
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
new file mode 100644
index 000000000..818f5d16a
--- /dev/null
+++ b/libunwindstack/ArmExidx.cpp
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Log.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+
+#include "ArmExidx.h"
+#include "Check.h"
+
+namespace unwindstack {
+
+static constexpr uint8_t LOG_CFA_REG = 64;
+
+void ArmExidx::LogRawData() {
+ std::string log_str("Raw Data:");
+ for (const uint8_t data : data_) {
+ log_str += android::base::StringPrintf(" 0x%02x", data);
+ }
+ log(log_indent_, log_str.c_str());
+}
+
+bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
+ data_.clear();
+ status_ = ARM_STATUS_NONE;
+
+ if (entry_offset & 1) {
+ // The offset needs to be at least two byte aligned.
+ status_ = ARM_STATUS_INVALID_ALIGNMENT;
+ return false;
+ }
+
+ // Each entry is a 32 bit prel31 offset followed by 32 bits
+ // of unwind information. If bit 31 of the unwind data is zero,
+ // then this is a prel31 offset to the start of the unwind data.
+ // If the unwind data is 1, then this is a cant unwind entry.
+ // Otherwise, this data is the compact form of the unwind information.
+ uint32_t data;
+ if (!elf_memory_->Read32(entry_offset + 4, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = entry_offset + 4;
+ return false;
+ }
+ if (data == 1) {
+ // This is a CANT UNWIND entry.
+ status_ = ARM_STATUS_NO_UNWIND;
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ }
+ log(log_indent_, "[cantunwind]");
+ }
+ return false;
+ }
+
+ if (data & (1UL << 31)) {
+ // This is a compact table entry.
+ if ((data >> 24) & 0xf) {
+ // This is a non-zero index, this code doesn't support
+ // other formats.
+ status_ = ARM_STATUS_INVALID_PERSONALITY;
+ return false;
+ }
+ data_.push_back((data >> 16) & 0xff);
+ data_.push_back((data >> 8) & 0xff);
+ uint8_t last_op = data & 0xff;
+ data_.push_back(last_op);
+ if (last_op != ARM_OP_FINISH) {
+ // If this didn't end with a finish op, add one.
+ data_.push_back(ARM_OP_FINISH);
+ }
+ if (log_type_ == ARM_LOG_FULL) {
+ LogRawData();
+ }
+ return true;
+ }
+
+ // Get the address of the ops.
+ // Sign extend the data value if necessary.
+ int32_t signed_data = static_cast<int32_t>(data << 1) >> 1;
+ uint32_t addr = (entry_offset + 4) + signed_data;
+ if (!elf_memory_->Read32(addr, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = addr;
+ return false;
+ }
+
+ size_t num_table_words;
+ if (data & (1UL << 31)) {
+ // Compact model.
+ switch ((data >> 24) & 0xf) {
+ case 0:
+ num_table_words = 0;
+ data_.push_back((data >> 16) & 0xff);
+ break;
+ case 1:
+ case 2:
+ num_table_words = (data >> 16) & 0xff;
+ addr += 4;
+ break;
+ default:
+ // Only a personality of 0, 1, 2 is valid.
+ status_ = ARM_STATUS_INVALID_PERSONALITY;
+ return false;
+ }
+ data_.push_back((data >> 8) & 0xff);
+ data_.push_back(data & 0xff);
+ } else {
+ // Generic model.
+
+ // Skip the personality routine data, it doesn't contain any data
+ // needed to decode the unwind information.
+ addr += 4;
+ if (!elf_memory_->Read32(addr, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = addr;
+ return false;
+ }
+ num_table_words = (data >> 24) & 0xff;
+ data_.push_back((data >> 16) & 0xff);
+ data_.push_back((data >> 8) & 0xff);
+ data_.push_back(data & 0xff);
+ addr += 4;
+ }
+
+ if (num_table_words > 5) {
+ status_ = ARM_STATUS_MALFORMED;
+ return false;
+ }
+
+ for (size_t i = 0; i < num_table_words; i++) {
+ if (!elf_memory_->Read32(addr, &data)) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = addr;
+ return false;
+ }
+ data_.push_back((data >> 24) & 0xff);
+ data_.push_back((data >> 16) & 0xff);
+ data_.push_back((data >> 8) & 0xff);
+ data_.push_back(data & 0xff);
+ addr += 4;
+ }
+
+ if (data_.back() != ARM_OP_FINISH) {
+ // If this didn't end with a finish op, add one.
+ data_.push_back(ARM_OP_FINISH);
+ }
+
+ if (log_type_ == ARM_LOG_FULL) {
+ LogRawData();
+ }
+ return true;
+}
+
+inline bool ArmExidx::GetByte(uint8_t* byte) {
+ if (data_.empty()) {
+ status_ = ARM_STATUS_TRUNCATED;
+ return false;
+ }
+ *byte = data_.front();
+ data_.pop_front();
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) {
+ CHECK((byte >> 4) == 0x8);
+
+ uint16_t registers = (byte & 0xf) << 8;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ registers |= byte;
+ if (registers == 0) {
+ // 10000000 00000000: Refuse to unwind
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Refuse to unwind");
+ }
+ status_ = ARM_STATUS_NO_UNWIND;
+ return false;
+ }
+ // 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
+ registers <<= 4;
+
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", reg);
+ add_comma = true;
+ }
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ uint32_t cfa_offset = __builtin_popcount(registers) * 4;
+ log_cfa_offset_ += cfa_offset;
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
+ }
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+
+ for (size_t reg = 4; reg < 16; reg++) {
+ if (registers & (1 << reg)) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = cfa_;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ }
+
+ // If the sp register is modified, change the cfa value.
+ if (registers & (1 << ARM_REG_SP)) {
+ cfa_ = (*regs_)[ARM_REG_SP];
+ }
+
+ // Indicate if the pc register was set.
+ if (registers & (1 << ARM_REG_PC)) {
+ pc_set_ = true;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_01(uint8_t byte) {
+ CHECK((byte >> 4) == 0x9);
+
+ uint8_t bits = byte & 0xf;
+ if (bits == 13 || bits == 15) {
+ // 10011101: Reserved as prefix for ARM register to register moves
+ // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "[Reserved]");
+ }
+ status_ = ARM_STATUS_RESERVED;
+ return false;
+ }
+ // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = r%d", bits);
+ } else {
+ log_regs_[LOG_CFA_REG] = bits;
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // It is impossible for bits to be larger than the total number of
+ // arm registers, so don't bother checking if bits is a valid register.
+ cfa_ = (*regs_)[bits];
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_10(uint8_t byte) {
+ CHECK((byte >> 4) == 0xa);
+
+ // 10100nnn: Pop r4-r[4+nnn]
+ // 10101nnn: Pop r4-r[4+nnn], r14
+ if (log_type_ != ARM_LOG_NONE) {
+ uint8_t end_reg = byte & 0x7;
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {r4";
+ if (end_reg) {
+ msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+ }
+ if (byte & 0x8) {
+ log(log_indent_, "%s, r14}", msg.c_str());
+ } else {
+ log(log_indent_, "%s}", msg.c_str());
+ }
+ } else {
+ end_reg += 4;
+ uint32_t cfa_offset = (end_reg - 3) * 4;
+ if (byte & 0x8) {
+ cfa_offset += 4;
+ }
+ log_cfa_offset_ += cfa_offset;
+
+ for (uint8_t reg = 4; reg <= end_reg; reg++) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
+
+ if (byte & 0x8) {
+ log_regs_[14] = cfa_offset;
+ }
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+
+ for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = cfa_;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ if (byte & 0x8) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = cfa_;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0000() {
+ // 10110000: Finish
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "finish");
+ }
+
+ if (log_skip_execution_) {
+ status_ = ARM_STATUS_FINISH;
+ return false;
+ }
+ }
+ status_ = ARM_STATUS_FINISH;
+ return false;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0001() {
+ uint8_t byte;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (byte == 0) {
+ // 10110001 00000000: Spare
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+ if (byte >> 4) {
+ // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+
+ // 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("r%zu", i);
+ add_comma = true;
+ }
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ byte &= 0xf;
+ uint32_t cfa_offset = __builtin_popcount(byte) * 4;
+ log_cfa_offset_ += cfa_offset;
+ for (size_t reg = 0; reg < 4; reg++) {
+ if (byte & (1 << reg)) {
+ log_regs_[reg] = cfa_offset;
+ cfa_offset -= 4;
+ }
+ }
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+
+ for (size_t reg = 0; reg < 4; reg++) {
+ if (byte & (1 << reg)) {
+ if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
+ status_ = ARM_STATUS_READ_FAILED;
+ status_address_ = cfa_;
+ return false;
+ }
+ cfa_ += 4;
+ }
+ }
+ return true;
+}
+
+inline void ArmExidx::AdjustRegisters(int32_t offset) {
+ for (auto& entry : log_regs_) {
+ if (entry.first >= LOG_CFA_REG) {
+ break;
+ }
+ entry.second += offset;
+ }
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0010() {
+ // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+ uint32_t result = 0;
+ uint32_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ result <<= 2;
+ if (log_type_ != ARM_LOG_NONE) {
+ int32_t cfa_offset = 0x204 + result;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ } else {
+ log_cfa_offset_ += cfa_offset;
+ }
+ AdjustRegisters(cfa_offset);
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ cfa_ += 0x204 + result;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_0011() {
+ // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+ uint8_t byte;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (log_type_ != ARM_LOG_NONE) {
+ uint8_t start_reg = byte >> 4;
+ uint8_t end_reg = start_reg + (byte & 0xf);
+
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ cfa_ += (byte & 0xf) * 8 + 12;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_01nn() {
+ // 101101nn: Spare
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+}
+
+inline bool ArmExidx::DecodePrefix_10_11_1nnn(uint8_t byte) {
+ CHECK((byte & ~0x07) == 0xb8);
+
+ // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t last_reg = (byte & 0x7);
+ std::string msg = "pop {d8";
+ if (last_reg) {
+ msg += android::base::StringPrintf("-d%d", last_reg + 8);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0x7) * 8 + 12;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_10(uint8_t byte) {
+ CHECK((byte >> 6) == 0x2);
+
+ switch ((byte >> 4) & 0x3) {
+ case 0:
+ return DecodePrefix_10_00(byte);
+ case 1:
+ return DecodePrefix_10_01(byte);
+ case 2:
+ return DecodePrefix_10_10(byte);
+ default:
+ switch (byte & 0xf) {
+ case 0:
+ return DecodePrefix_10_11_0000();
+ case 1:
+ return DecodePrefix_10_11_0001();
+ case 2:
+ return DecodePrefix_10_11_0010();
+ case 3:
+ return DecodePrefix_10_11_0011();
+ default:
+ if (byte & 0x8) {
+ return DecodePrefix_10_11_1nnn(byte);
+ } else {
+ return DecodePrefix_10_11_01nn();
+ }
+ }
+ }
+}
+
+inline bool ArmExidx::DecodePrefix_11_000(uint8_t byte) {
+ CHECK((byte & ~0x07) == 0xc0);
+
+ uint8_t bits = byte & 0x7;
+ if (bits == 6) {
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wRX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0xf) * 8 + 8;
+ } else if (bits == 7) {
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (byte == 0) {
+ // 11000111 00000000: Spare
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ } else if ((byte >> 4) == 0) {
+ // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ bool add_comma = false;
+ std::string msg = "pop {";
+ for (size_t i = 0; i < 4; i++) {
+ if (byte & (1 << i)) {
+ if (add_comma) {
+ msg += ", ";
+ }
+ msg += android::base::StringPrintf("wCGR%zu", i);
+ add_comma = true;
+ }
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wCGR register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += __builtin_popcount(byte) * 4;
+ } else {
+ // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+ } else {
+ // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {wR10";
+ uint8_t nnn = byte & 0x7;
+ if (nnn) {
+ msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported wRX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0x7) * 8 + 8;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11_001(uint8_t byte) {
+ CHECK((byte & ~0x07) == 0xc8);
+
+ uint8_t bits = byte & 0x7;
+ if (bits == 0) {
+ // 11001000 sssscccc: Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] by VPUSH
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0xf) * 8 + 8;
+ } else if (bits == 1) {
+ // 11001001 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by VPUSH
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ uint8_t start_reg = byte >> 4;
+ std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+ uint8_t end_reg = byte & 0xf;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ // Only update the cfa.
+ cfa_ += (byte & 0xf) * 8 + 8;
+ } else {
+ // 11001yyy: Spare (yyy != 000, 001)
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11_010(uint8_t byte) {
+ CHECK((byte & ~0x07) == 0xd0);
+
+ // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+ if (log_type_ != ARM_LOG_NONE) {
+ if (log_type_ == ARM_LOG_FULL) {
+ std::string msg = "pop {d8";
+ uint8_t end_reg = byte & 0x7;
+ if (end_reg) {
+ msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+ }
+ log(log_indent_, "%s}", msg.c_str());
+ } else {
+ log(log_indent_, "Unsupported DX register display");
+ }
+
+ if (log_skip_execution_) {
+ return true;
+ }
+ }
+ cfa_ += (byte & 0x7) * 8 + 8;
+ return true;
+}
+
+inline bool ArmExidx::DecodePrefix_11(uint8_t byte) {
+ CHECK((byte >> 6) == 0x3);
+
+ switch ((byte >> 3) & 0x7) {
+ case 0:
+ return DecodePrefix_11_000(byte);
+ case 1:
+ return DecodePrefix_11_001(byte);
+ case 2:
+ return DecodePrefix_11_010(byte);
+ default:
+ // 11xxxyyy: Spare (xxx != 000, 001, 010)
+ if (log_type_ != ARM_LOG_NONE) {
+ log(log_indent_, "Spare");
+ }
+ status_ = ARM_STATUS_SPARE;
+ return false;
+ }
+}
+
+bool ArmExidx::Decode() {
+ status_ = ARM_STATUS_NONE;
+ uint8_t byte;
+ if (!GetByte(&byte)) {
+ return false;
+ }
+
+ switch (byte >> 6) {
+ case 0:
+ // 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
+ if (log_type_ != ARM_LOG_NONE) {
+ int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ } else {
+ log_cfa_offset_ += cfa_offset;
+ }
+ AdjustRegisters(cfa_offset);
+
+ if (log_skip_execution_) {
+ break;
+ }
+ }
+ cfa_ += ((byte & 0x3f) << 2) + 4;
+ break;
+ case 1:
+ // 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
+ if (log_type_ != ARM_LOG_NONE) {
+ uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+ if (log_type_ == ARM_LOG_FULL) {
+ log(log_indent_, "vsp = vsp - %d", cfa_offset);
+ } else {
+ log_cfa_offset_ -= cfa_offset;
+ }
+ AdjustRegisters(-cfa_offset);
+
+ if (log_skip_execution_) {
+ break;
+ }
+ }
+ cfa_ -= ((byte & 0x3f) << 2) + 4;
+ break;
+ case 2:
+ return DecodePrefix_10(byte);
+ default:
+ return DecodePrefix_11(byte);
+ }
+ return true;
+}
+
+bool ArmExidx::Eval() {
+ pc_set_ = false;
+ while (Decode());
+ return status_ == ARM_STATUS_FINISH;
+}
+
+void ArmExidx::LogByReg() {
+ if (log_type_ != ARM_LOG_BY_REG) {
+ return;
+ }
+
+ uint8_t cfa_reg;
+ if (log_regs_.count(LOG_CFA_REG) == 0) {
+ cfa_reg = 13;
+ } else {
+ cfa_reg = log_regs_[LOG_CFA_REG];
+ }
+
+ if (log_cfa_offset_ != 0) {
+ char sign = (log_cfa_offset_ > 0) ? '+' : '-';
+ log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+ } else {
+ log(log_indent_, "cfa = r%zu", cfa_reg);
+ }
+
+ for (const auto& entry : log_regs_) {
+ if (entry.first >= LOG_CFA_REG) {
+ break;
+ }
+ if (entry.second == 0) {
+ log(log_indent_, "r%zu = [cfa]", entry.first);
+ } else {
+ char sign = (entry.second > 0) ? '-' : '+';
+ log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+ }
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
new file mode 100644
index 000000000..d9fc371c3
--- /dev/null
+++ b/libunwindstack/ArmExidx.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ARM_EXIDX_H
+#define _LIBUNWINDSTACK_ARM_EXIDX_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <map>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+class RegsArm;
+
+enum ArmStatus : size_t {
+ ARM_STATUS_NONE = 0,
+ ARM_STATUS_NO_UNWIND,
+ ARM_STATUS_FINISH,
+ ARM_STATUS_RESERVED,
+ ARM_STATUS_SPARE,
+ ARM_STATUS_TRUNCATED,
+ ARM_STATUS_READ_FAILED,
+ ARM_STATUS_MALFORMED,
+ ARM_STATUS_INVALID_ALIGNMENT,
+ ARM_STATUS_INVALID_PERSONALITY,
+};
+
+enum ArmOp : uint8_t {
+ ARM_OP_FINISH = 0xb0,
+};
+
+enum ArmLogType : uint8_t {
+ ARM_LOG_NONE,
+ ARM_LOG_FULL,
+ ARM_LOG_BY_REG,
+};
+
+class ArmExidx {
+ public:
+ ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
+ : regs_(regs), elf_memory_(elf_memory), process_memory_(process_memory) {}
+ virtual ~ArmExidx() {}
+
+ void LogRawData();
+
+ void LogByReg();
+
+ bool ExtractEntryData(uint32_t entry_offset);
+
+ bool Eval();
+
+ bool Decode();
+
+ std::deque<uint8_t>* data() { return &data_; }
+
+ ArmStatus status() { return status_; }
+ uint64_t status_address() { return status_address_; }
+
+ RegsArm* regs() { return regs_; }
+
+ uint32_t cfa() { return cfa_; }
+ void set_cfa(uint32_t cfa) { cfa_ = cfa; }
+
+ bool pc_set() { return pc_set_; }
+ void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
+
+ void set_log(ArmLogType log_type) { log_type_ = log_type; }
+ void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
+ void set_log_indent(uint8_t indent) { log_indent_ = indent; }
+
+ private:
+ bool GetByte(uint8_t* byte);
+ void AdjustRegisters(int32_t offset);
+
+ bool DecodePrefix_10_00(uint8_t byte);
+ bool DecodePrefix_10_01(uint8_t byte);
+ bool DecodePrefix_10_10(uint8_t byte);
+ bool DecodePrefix_10_11_0000();
+ bool DecodePrefix_10_11_0001();
+ bool DecodePrefix_10_11_0010();
+ bool DecodePrefix_10_11_0011();
+ bool DecodePrefix_10_11_01nn();
+ bool DecodePrefix_10_11_1nnn(uint8_t byte);
+ bool DecodePrefix_10(uint8_t byte);
+
+ bool DecodePrefix_11_000(uint8_t byte);
+ bool DecodePrefix_11_001(uint8_t byte);
+ bool DecodePrefix_11_010(uint8_t byte);
+ bool DecodePrefix_11(uint8_t byte);
+
+ RegsArm* regs_ = nullptr;
+ uint32_t cfa_ = 0;
+ std::deque<uint8_t> data_;
+ ArmStatus status_ = ARM_STATUS_NONE;
+ uint64_t status_address_ = 0;
+
+ Memory* elf_memory_;
+ Memory* process_memory_;
+
+ ArmLogType log_type_ = ARM_LOG_NONE;
+ uint8_t log_indent_ = 0;
+ bool log_skip_execution_ = false;
+ bool pc_set_ = false;
+ int32_t log_cfa_offset_ = 0;
+ std::map<uint8_t, int32_t> log_regs_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsMips.S b/libunwindstack/AsmGetRegsMips.S
new file mode 100644
index 000000000..183d0a928
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .type AsmGetRegs, %function
+ .globl AsmGetRegs
+ .ent AsmGetRegs
+ .balign 16
+AsmGetRegs:
+ .cfi_startproc
+ .cfi_def_cfa $sp, 0
+ .set push
+ .set noreorder
+ .cpload $t9
+ sw $zero, 0($a0)
+ .set noat
+ sw $at, 4($a0)
+ .set at
+ sw $v0, 8($a0)
+ sw $v1, 12($a0)
+ sw $a0, 16($a0)
+ sw $a1, 20($a0)
+ sw $a2, 24($a0)
+ sw $a3, 28($a0)
+ sw $t0, 32($a0)
+ sw $t1, 36($a0)
+ sw $t2, 40($a0)
+ sw $t3, 44($a0)
+ sw $t4, 48($a0)
+ sw $t5, 52($a0)
+ sw $t6, 56($a0)
+ sw $t7, 60($a0)
+ sw $s0, 64($a0)
+ sw $s1, 68($a0)
+ sw $s2, 72($a0)
+ sw $s3, 76($a0)
+ sw $s4, 80($a0)
+ sw $s5, 84($a0)
+ sw $s6, 88($a0)
+ sw $s7, 92($a0)
+ sw $t8, 96($a0)
+ sw $t9, 100($a0)
+ sw $k0, 104($a0)
+ sw $k1, 108($a0)
+ sw $gp, 112($a0)
+ sw $sp, 116($a0)
+ sw $s8, 120($a0)
+ sw $ra, 124($a0)
+ jalr $zero, $ra
+ sw $ra, 128($a0) // set PC to the calling function
+
+ .set pop
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
+ .end AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsMips64.S b/libunwindstack/AsmGetRegsMips64.S
new file mode 100644
index 000000000..7a244f6f8
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips64.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .type AsmGetRegs, %function
+ .globl AsmGetRegs
+ .ent AsmGetRegs
+ .balign 16
+AsmGetRegs:
+ .cfi_startproc
+ .cfi_def_cfa $sp, 0
+ .set push
+ .set noreorder
+ .cpload $t9
+ sd $zero, 0($a0)
+ .set noat
+ sd $at, 8($a0)
+ .set at
+ sd $v0, 16($a0)
+ sd $v1, 24($a0)
+ sd $a0, 32($a0)
+ sd $a1, 40($a0)
+ sd $a2, 48($a0)
+ sd $a3, 56($a0)
+ sd $a4, 64($a0)
+ sd $a5, 72($a0)
+ sd $a6, 80($a0)
+ sd $a7, 88($a0)
+ sd $t0, 96($a0)
+ sd $t1, 104($a0)
+ sd $t2, 112($a0)
+ sd $t3, 120($a0)
+ sd $s0, 128($a0)
+ sd $s1, 136($a0)
+ sd $s2, 144($a0)
+ sd $s3, 152($a0)
+ sd $s4, 160($a0)
+ sd $s5, 168($a0)
+ sd $s6, 176($a0)
+ sd $s7, 184($a0)
+ sd $t8, 192($a0)
+ sd $t9, 200($a0)
+ sd $k0, 208($a0)
+ sd $k1, 216($a0)
+ sd $gp, 224($a0)
+ sd $sp, 232($a0)
+ sd $s8, 240($a0)
+ sd $ra, 248($a0)
+ jalr $zero, $ra
+ sd $ra, 256($a0) // set PC to the calling function
+
+ .set pop
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
+ .end AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 000000000..021e6286c
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ mov 4(%esp), %eax
+ movl $0, (%eax)
+ movl %ecx, 4(%eax)
+ movl %edx, 8(%eax)
+ movl %ebx, 12(%eax)
+
+ /* ESP */
+ leal 4(%esp), %ecx
+ movl %ecx, 16(%eax)
+
+ movl %ebp, 20(%eax)
+ movl %esi, 24(%eax)
+ movl %edi, 28(%eax)
+
+ /* EIP */
+ movl (%esp), %ecx
+ movl %ecx, 32(%eax)
+
+ mov %cs, 36(%eax)
+ mov %ss, 40(%eax)
+ mov %ds, 44(%eax)
+ mov %es, 48(%eax)
+ mov %fs, 52(%eax)
+ mov %gs, 56(%eax)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 000000000..4cd3b6fb6
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ movq %rax, (%rdi)
+ movq %rdx, 8(%rdi)
+ movq %rcx, 16(%rdi)
+ movq %rbx, 24(%rdi)
+ movq %rsi, 32(%rdi)
+ movq %rdi, 40(%rdi)
+ movq %rbp, 48(%rdi)
+
+ /* RSP */
+ lea 8(%rsp), %rax
+ movq %rax, 56(%rdi)
+
+ movq %r8, 64(%rdi)
+ movq %r9, 72(%rdi)
+ movq %r10, 80(%rdi)
+ movq %r11, 88(%rdi)
+ movq %r12, 96(%rdi)
+ movq %r13, 104(%rdi)
+ movq %r14, 112(%rdi)
+ movq %r15, 120(%rdi)
+
+ /* RIP */
+ movq (%rsp), %rax
+ movq %rax, 128(%rdi)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
new file mode 100644
index 000000000..9643d7670
--- /dev/null
+++ b/libunwindstack/Check.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_CHECK_H
+#define _LIBUNWINDSTACK_CHECK_H
+
+#include <stdlib.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+#define CHECK(assertion) \
+ if (__builtin_expect(!(assertion), false)) { \
+ log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+ abort(); \
+ }
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_CHECK_H
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
new file mode 100644
index 000000000..bf63abf1c
--- /dev/null
+++ b/libunwindstack/DexFile.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
+#include <android-base/unique_fd.h>
+#include <art_api/dex_file_support.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFile.h"
+
+namespace unwindstack {
+
+static bool CheckDexSupport() {
+ if (std::string err_msg; !art_api::dex::TryLoadLibdexfileExternal(&err_msg)) {
+ ALOGW("Failed to initialize DEX file support: %s", err_msg.c_str());
+ return false;
+ }
+ return true;
+}
+
+static bool HasDexSupport() {
+ static bool has_dex_support = CheckDexSupport();
+ return has_dex_support;
+}
+
+std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+ MapInfo* info) {
+ if (!info->name.empty()) {
+ std::unique_ptr<DexFile> dex_file =
+ DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
+ if (dex_file) {
+ return dex_file;
+ }
+ }
+ return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
+}
+
+bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
+ uint64_t* method_offset) {
+ art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
+ if (method_info.offset == 0) {
+ return false;
+ }
+ *method_name = method_info.name;
+ *method_offset = dex_offset - method_info.offset;
+ return true;
+}
+
+std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
+ const std::string& file) {
+ if (UNLIKELY(!HasDexSupport())) {
+ return nullptr;
+ }
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ return nullptr;
+ }
+
+ std::string error_msg;
+ std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+ OpenFromFd(fd, dex_file_offset_in_file, file, &error_msg);
+ if (art_dex_file == nullptr) {
+ return nullptr;
+ }
+
+ return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(art_dex_file));
+}
+
+std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
+ Memory* memory,
+ const std::string& name) {
+ if (UNLIKELY(!HasDexSupport())) {
+ return nullptr;
+ }
+
+ std::vector<uint8_t> backing_memory;
+
+ for (size_t size = 0;;) {
+ std::string error_msg;
+ std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+ OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+
+ if (art_dex_file != nullptr) {
+ return std::unique_ptr<DexFileFromMemory>(
+ new DexFileFromMemory(art_dex_file, std::move(backing_memory)));
+ }
+
+ if (!error_msg.empty()) {
+ return nullptr;
+ }
+
+ backing_memory.resize(size);
+ if (!memory->ReadFully(dex_file_offset_in_memory, backing_memory.data(),
+ backing_memory.size())) {
+ return nullptr;
+ }
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
new file mode 100644
index 000000000..4e8369f84
--- /dev/null
+++ b/libunwindstack/DexFile.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DEX_FILE_H
+#define _LIBUNWINDSTACK_DEX_FILE_H
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <art_api/dex_file_support.h>
+
+namespace unwindstack {
+
+class DexFile : protected art_api::dex::DexFile {
+ public:
+ virtual ~DexFile() = default;
+
+ bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
+
+ static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+ MapInfo* info);
+
+ protected:
+ DexFile(std::unique_ptr<art_api::dex::DexFile>& art_dex_file)
+ : art_api::dex::DexFile(art_dex_file) {}
+};
+
+class DexFileFromFile : public DexFile {
+ public:
+ static std::unique_ptr<DexFileFromFile> Create(uint64_t dex_file_offset_in_file,
+ const std::string& file);
+
+ private:
+ DexFileFromFile(std::unique_ptr<art_api::dex::DexFile>& art_dex_file) : DexFile(art_dex_file) {}
+};
+
+class DexFileFromMemory : public DexFile {
+ public:
+ static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
+ Memory* memory, const std::string& name);
+
+ private:
+ DexFileFromMemory(std::unique_ptr<art_api::dex::DexFile>& art_dex_file,
+ std::vector<uint8_t>&& memory)
+ : DexFile(art_dex_file), memory_(std::move(memory)) {}
+
+ std::vector<uint8_t> memory_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DEX_FILE_H
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
new file mode 100644
index 000000000..2057fad19
--- /dev/null
+++ b/libunwindstack/DexFiles.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#if defined(DEXFILE_SUPPORT)
+#include "DexFile.h"
+#endif
+
+namespace unwindstack {
+
+#if !defined(DEXFILE_SUPPORT)
+// Empty class definition.
+class DexFile {
+ public:
+ DexFile() = default;
+ virtual ~DexFile() = default;
+};
+#endif
+
+struct DEXFileEntry32 {
+ uint32_t next;
+ uint32_t prev;
+ uint32_t dex_file;
+};
+
+struct DEXFileEntry64 {
+ uint64_t next;
+ uint64_t prev;
+ uint64_t dex_file;
+};
+
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {}
+
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+ : Global(memory, search_libs) {}
+
+DexFiles::~DexFiles() {}
+
+void DexFiles::ProcessArch() {
+ switch (arch()) {
+ case ARCH_ARM:
+ case ARCH_MIPS:
+ case ARCH_X86:
+ read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
+ read_entry_func_ = &DexFiles::ReadEntry32;
+ break;
+
+ case ARCH_ARM64:
+ case ARCH_MIPS64:
+ case ARCH_X86_64:
+ read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
+ read_entry_func_ = &DexFiles::ReadEntry64;
+ break;
+
+ case ARCH_UNKNOWN:
+ abort();
+ }
+}
+
+uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
+ uint32_t entry;
+ const uint32_t field_offset = 12; // offset of first_entry_ in the descriptor struct.
+ if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
+ return 0;
+ }
+ return entry;
+}
+
+uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
+ uint64_t entry;
+ const uint32_t field_offset = 16; // offset of first_entry_ in the descriptor struct.
+ if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
+ return 0;
+ }
+ return entry;
+}
+
+bool DexFiles::ReadEntry32() {
+ DEXFileEntry32 entry;
+ if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+ entry_addr_ = 0;
+ return false;
+ }
+
+ addrs_.push_back(entry.dex_file);
+ entry_addr_ = entry.next;
+ return true;
+}
+
+bool DexFiles::ReadEntry64() {
+ DEXFileEntry64 entry;
+ if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+ entry_addr_ = 0;
+ return false;
+ }
+
+ addrs_.push_back(entry.dex_file);
+ entry_addr_ = entry.next;
+ return true;
+}
+
+bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
+ entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
+ return entry_addr_ != 0;
+}
+
+void DexFiles::Init(Maps* maps) {
+ if (initialized_) {
+ return;
+ }
+ initialized_ = true;
+ entry_addr_ = 0;
+
+ FindAndReadVariable(maps, "__dex_debug_descriptor");
+}
+
+#if defined(DEXFILE_SUPPORT)
+DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
+ // Lock while processing the data.
+ DexFile* dex_file;
+ auto entry = files_.find(dex_file_offset);
+ if (entry == files_.end()) {
+ std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+ dex_file = new_dex_file.get();
+ files_[dex_file_offset] = std::move(new_dex_file);
+ } else {
+ dex_file = entry->second.get();
+ }
+ return dex_file;
+}
+#else
+DexFile* DexFiles::GetDexFile(uint64_t, MapInfo*) {
+ return nullptr;
+}
+#endif
+
+bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
+ if (index < addrs_.size()) {
+ *addr = addrs_[index];
+ return true;
+ }
+ if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
+ *addr = addrs_.back();
+ return true;
+ }
+ return false;
+}
+
+#if defined(DEXFILE_SUPPORT)
+void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
+ std::string* method_name, uint64_t* method_offset) {
+ std::lock_guard<std::mutex> guard(lock_);
+ if (!initialized_) {
+ Init(maps);
+ }
+
+ size_t index = 0;
+ uint64_t addr;
+ while (GetAddr(index++, &addr)) {
+ if (addr < info->start || addr >= info->end) {
+ continue;
+ }
+
+ DexFile* dex_file = GetDexFile(addr, info);
+ if (dex_file != nullptr &&
+ dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
+ break;
+ }
+ }
+}
+#else
+void DexFiles::GetMethodInformation(Maps*, MapInfo*, uint64_t, std::string*, uint64_t*) {}
+#endif
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
new file mode 100644
index 000000000..c128b9bb7
--- /dev/null
+++ b/libunwindstack/DwarfCfa.cpp
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+template <typename AddressType>
+constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs) {
+ if (cie_loc_regs_ != nullptr) {
+ for (const auto& entry : *cie_loc_regs_) {
+ (*loc_regs)[entry.first] = entry.second;
+ }
+ }
+ last_error_.code = DWARF_ERROR_NONE;
+ last_error_.address = 0;
+
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ cur_pc_ = fde_->pc_start;
+ loc_regs->pc_start = cur_pc_;
+ while (true) {
+ if (cur_pc_ > pc) {
+ loc_regs->pc_end = cur_pc_;
+ return true;
+ }
+ if ((cfa_offset = memory_->cur_offset()) >= end_offset) {
+ loc_regs->pc_end = fde_->pc_end;
+ return true;
+ }
+ loc_regs->pc_start = cur_pc_;
+ operands_.clear();
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_->cur_offset();
+ return false;
+ }
+ uint8_t cfa_low = cfa_value & 0x3f;
+ // Check the 2 high bits.
+ switch (cfa_value >> 6) {
+ case 1:
+ cur_pc_ += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2: {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_->cur_offset();
+ return false;
+ }
+ SignedType signed_offset =
+ static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET,
+ .values = {static_cast<uint64_t>(signed_offset)}};
+ break;
+ }
+ case 3: {
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ auto reg_entry = cie_loc_regs_->find(cfa_low);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(cfa_low);
+ } else {
+ (*loc_regs)[cfa_low] = reg_entry->second;
+ }
+ break;
+ }
+ case 0: {
+ const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
+ if (handle_func == nullptr) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ const auto cfa = &DwarfCfaInfo::kTable[cfa_low];
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ uint64_t block_length;
+ if (!memory_->ReadULEB128(&block_length)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_->cur_offset();
+ return false;
+ }
+ operands_.push_back(block_length);
+ memory_->set_cur_offset(memory_->cur_offset() + block_length);
+ continue;
+ }
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_->cur_offset();
+ return false;
+ }
+ operands_.push_back(value);
+ }
+
+ if (!(this->*handle_func)(loc_regs)) {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+}
+
+template <typename AddressType>
+std::string DwarfCfa<AddressType>::GetOperandString(uint8_t operand, uint64_t value,
+ uint64_t* cur_pc) {
+ std::string string;
+ switch (operand) {
+ case DwarfCfaInfo::DWARF_DISPLAY_REGISTER:
+ string = " register(" + std::to_string(value) + ")";
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER:
+ string += " " + std::to_string(static_cast<SignedType>(value));
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
+ *cur_pc += value;
+ FALLTHROUGH_INTENDED;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
+ string += " " + std::to_string(value);
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
+ *cur_pc = value;
+ FALLTHROUGH_INTENDED;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
+ if (std::is_same<AddressType, uint32_t>::value) {
+ string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
+ } else {
+ string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value));
+ }
+ break;
+ default:
+ string = " unknown";
+ }
+ return string;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset,
+ uint8_t reg) {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ return false;
+ }
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+
+ std::string raw_data = "Raw Data:";
+ for (uint64_t i = cfa_offset; i < end_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+ log(indent, "%s", raw_data.c_str());
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
+ uint64_t* cur_pc) {
+ const auto* cfa = &DwarfCfaInfo::kTable[op];
+ if (cfa->name[0] == '\0') {
+ log(indent, "Illegal");
+ log(indent, "Raw Data: 0x%02x", op);
+ return true;
+ }
+
+ std::string log_string(cfa->name);
+ std::vector<std::string> expression_lines;
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ // This is a Dwarf Expression.
+ uint64_t end_offset;
+ if (!memory_->ReadULEB128(&end_offset)) {
+ return false;
+ }
+ log_string += " " + std::to_string(end_offset);
+ end_offset += memory_->cur_offset();
+
+ DwarfOp<AddressType> op(memory_, nullptr);
+ op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines);
+ memory_->set_cur_offset(end_offset);
+ } else {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ return false;
+ }
+ log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
+ }
+ }
+ log(indent, "%s", log_string.c_str());
+
+ // Get the raw bytes of the data.
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+ std::string raw_data("Raw Data:");
+ for (uint64_t i = 0; i < end_offset - cfa_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+
+ // Only show 10 raw bytes per line.
+ if ((i % 10) == 0 && i != 0) {
+ log(indent, "%s", raw_data.c_str());
+ raw_data.clear();
+ }
+ if (raw_data.empty()) {
+ raw_data = "Raw Data:";
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ if (!raw_data.empty()) {
+ log(indent, "%s", raw_data.c_str());
+ }
+
+ // Log any of the expression data.
+ for (const auto& line : expression_lines) {
+ log(indent + 1, "%s", line.c_str());
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
+ uint64_t end_offset) {
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ uint64_t cur_pc = fde_->pc_start;
+ uint64_t old_pc = cur_pc;
+ while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) {
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ return false;
+ }
+
+ // Check the 2 high bits.
+ uint8_t cfa_low = cfa_value & 0x3f;
+ switch (cfa_value >> 6) {
+ case 0:
+ if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) {
+ return false;
+ }
+ break;
+ case 1:
+ log(indent, "DW_CFA_advance_loc %d", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ cur_pc += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2:
+ if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) {
+ return false;
+ }
+ break;
+ case 3:
+ log(indent, "DW_CFA_restore register(%d)", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ break;
+ }
+ if (cur_pc != old_pc) {
+ log(0, "");
+ log(indent, "PC 0x%" PRIx64, cur_pc);
+ }
+ old_pc = cur_pc;
+ }
+ return true;
+}
+
+// Static data.
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_set_loc(dwarf_loc_regs_t*) {
+ AddressType cur_pc = cur_pc_;
+ AddressType new_pc = operands_[0];
+ if (new_pc < cur_pc) {
+ if (std::is_same<AddressType, uint32_t>::value) {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+ } else {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
+ }
+ }
+ cur_pc_ = new_pc;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) {
+ cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ auto reg_entry = cie_loc_regs_->find(reg);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(reg);
+ } else {
+ (*loc_regs)[reg] = reg_entry->second;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ loc_regs->erase(reg);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_register(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ AddressType reg_dst = operands_[1];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) {
+ loc_reg_state_.push(*loc_regs);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore_state(dwarf_loc_regs_t* loc_regs) {
+ if (loc_reg_state_.size() == 0) {
+ log(0, "Warning: Attempt to restore without remember.");
+ return true;
+ }
+ *loc_regs = loc_reg_state_.top();
+ loc_reg_state_.pop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) {
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set new register, but cfa is not already set to a register.");
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ cfa_location->second.values[0] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ cfa_location->second.values[1] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ // There is only one type of expression for CFA evaluation and the DWARF
+ // specification is unclear whether it returns the address or the
+ // dereferenced value. GDB expects the value, so will we.
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
+ .values = {operands_[0], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) {
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER,
+ .values = {operands_[0], static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
+ cfa_location->second.values[1] = static_cast<uint64_t>(offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = -static_cast<SignedType>(operands_[1]);
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
+ {
+ // 0x00 DW_CFA_nop
+ "DW_CFA_nop",
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_set_loc", // 0x01 DW_CFA_set_loc
+ 2,
+ 1,
+ {DW_EH_PE_absptr},
+ {DWARF_DISPLAY_SET_LOC},
+ },
+ {
+ "DW_CFA_advance_loc1", // 0x02 DW_CFA_advance_loc1
+ 2,
+ 1,
+ {DW_EH_PE_udata1},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc2", // 0x03 DW_CFA_advance_loc2
+ 2,
+ 1,
+ {DW_EH_PE_udata2},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc4", // 0x04 DW_CFA_advance_loc4
+ 2,
+ 1,
+ {DW_EH_PE_udata4},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_offset_extended", // 0x05 DW_CFA_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_restore_extended", // 0x06 DW_CFA_restore_extended
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_undefined", // 0x07 DW_CFA_undefined
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_same_value", // 0x08 DW_CFA_same_value
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_register", // 0x09 DW_CFA_register
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_remember_state", // 0x0a DW_CFA_remember_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_restore_state", // 0x0b DW_CFA_restore_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_def_cfa", // 0x0c DW_CFA_def_cfa
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_register", // 0x0d DW_CFA_def_cfa_register
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_def_cfa_offset", // 0x0e DW_CFA_def_cfa_offset
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_expression", // 0x0f DW_CFA_def_cfa_expression
+ 2,
+ 1,
+ {DW_EH_PE_block},
+ {DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_expression", // 0x10 DW_CFA_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_offset_extended_sf", // 0x11 DW_CFA_offset_extend_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_sf", // 0x12 DW_CFA_def_cfa_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_offset_sf", // 0x13 DW_CFA_def_cfa_offset_sf
+ 2,
+ 1,
+ {DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset", // 0x14 DW_CFA_val_offset
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset_sf", // 0x15 DW_CFA_val_offset_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_expression", // 0x16 DW_CFA_val_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {"", 0, 0, {}, {}}, // 0x17 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x18 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x19 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x1d illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1e illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1f illegal cfa
+ {"", 0, 0, {}, {}}, // 0x20 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x21 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x22 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x23 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x24 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x25 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x26 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x27 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x28 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x29 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2c illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {
+ "DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_GNU_negative_offset_extended", // 0x2f DW_CFA_GNU_negative_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {"", 0, 0, {}, {}}, // 0x31 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x32 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x33 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x34 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x35 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x36 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x37 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x38 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x39 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3c illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3d illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3e illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
+};
+
+// Explicitly instantiate DwarfCfa.
+template class DwarfCfa<uint32_t>;
+template class DwarfCfa<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
new file mode 100644
index 000000000..569c17cef
--- /dev/null
+++ b/libunwindstack/DwarfCfa.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_CFA_H
+#define _LIBUNWINDSTACK_DWARF_CFA_H
+
+#include <stdint.h>
+
+#include <stack>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
+
+// DWARF Standard home: http://dwarfstd.org/
+// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
+// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
+
+class DwarfCfaInfo {
+ public:
+ enum DisplayType : uint8_t {
+ DWARF_DISPLAY_NONE = 0,
+ DWARF_DISPLAY_REGISTER,
+ DWARF_DISPLAY_NUMBER,
+ DWARF_DISPLAY_SIGNED_NUMBER,
+ DWARF_DISPLAY_EVAL_BLOCK,
+ DWARF_DISPLAY_ADDRESS,
+ DWARF_DISPLAY_SET_LOC,
+ DWARF_DISPLAY_ADVANCE_LOC,
+ };
+
+ struct Info {
+ // It may seem cleaner to just change the type of 'name' to 'const char *'.
+ // However, having a pointer here would require relocation at runtime,
+ // causing 'kTable' to be placed in data.rel.ro section instead of rodata
+ // section, adding memory pressure to the system. Note that this is only
+ // safe because this is only used in C++ code. C++ standard, unlike C
+ // standard, mandates the array size to be large enough to hold the NULL
+ // terminator when initialized with a string literal.
+ const char name[36];
+ uint8_t supported_version;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ uint8_t display_operands[2];
+ };
+
+ const static Info kTable[64];
+};
+
+template <typename AddressType>
+class DwarfCfa {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ public:
+ DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
+ virtual ~DwarfCfa() = default;
+
+ bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs);
+
+ bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
+
+ const DwarfErrorData& last_error() { return last_error_; }
+ DwarfErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ AddressType cur_pc() { return cur_pc_; }
+
+ void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; }
+
+ protected:
+ std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc);
+
+ bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg);
+
+ bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
+
+ private:
+ DwarfErrorData last_error_;
+ DwarfMemory* memory_;
+ const DwarfFde* fde_;
+
+ AddressType cur_pc_;
+ const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
+ std::vector<AddressType> operands_;
+ std::stack<dwarf_loc_regs_t> loc_reg_state_;
+
+ // CFA processing functions.
+ bool cfa_nop(dwarf_loc_regs_t*);
+ bool cfa_set_loc(dwarf_loc_regs_t*);
+ bool cfa_advance_loc(dwarf_loc_regs_t*);
+ bool cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_restore(dwarf_loc_regs_t*);
+ bool cfa_undefined(dwarf_loc_regs_t*);
+ bool cfa_same_value(dwarf_loc_regs_t*);
+ bool cfa_register(dwarf_loc_regs_t*);
+ bool cfa_remember_state(dwarf_loc_regs_t*);
+ bool cfa_restore_state(dwarf_loc_regs_t*);
+ bool cfa_def_cfa(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_register(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_offset_extended_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_offset(dwarf_loc_regs_t*);
+ bool cfa_val_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_expression(dwarf_loc_regs_t*);
+ bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+
+ using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
+ constexpr static process_func kCallbackTable[64] = {
+ // 0x00 DW_CFA_nop
+ &DwarfCfa::cfa_nop,
+ // 0x01 DW_CFA_set_loc
+ &DwarfCfa::cfa_set_loc,
+ // 0x02 DW_CFA_advance_loc1
+ &DwarfCfa::cfa_advance_loc,
+ // 0x03 DW_CFA_advance_loc2
+ &DwarfCfa::cfa_advance_loc,
+ // 0x04 DW_CFA_advance_loc4
+ &DwarfCfa::cfa_advance_loc,
+ // 0x05 DW_CFA_offset_extended
+ &DwarfCfa::cfa_offset,
+ // 0x06 DW_CFA_restore_extended
+ &DwarfCfa::cfa_restore,
+ // 0x07 DW_CFA_undefined
+ &DwarfCfa::cfa_undefined,
+ // 0x08 DW_CFA_same_value
+ &DwarfCfa::cfa_same_value,
+ // 0x09 DW_CFA_register
+ &DwarfCfa::cfa_register,
+ // 0x0a DW_CFA_remember_state
+ &DwarfCfa::cfa_remember_state,
+ // 0x0b DW_CFA_restore_state
+ &DwarfCfa::cfa_restore_state,
+ // 0x0c DW_CFA_def_cfa
+ &DwarfCfa::cfa_def_cfa,
+ // 0x0d DW_CFA_def_cfa_register
+ &DwarfCfa::cfa_def_cfa_register,
+ // 0x0e DW_CFA_def_cfa_offset
+ &DwarfCfa::cfa_def_cfa_offset,
+ // 0x0f DW_CFA_def_cfa_expression
+ &DwarfCfa::cfa_def_cfa_expression,
+ // 0x10 DW_CFA_expression
+ &DwarfCfa::cfa_expression,
+ // 0x11 DW_CFA_offset_extended_sf
+ &DwarfCfa::cfa_offset_extended_sf,
+ // 0x12 DW_CFA_def_cfa_sf
+ &DwarfCfa::cfa_def_cfa_sf,
+ // 0x13 DW_CFA_def_cfa_offset_sf
+ &DwarfCfa::cfa_def_cfa_offset_sf,
+ // 0x14 DW_CFA_val_offset
+ &DwarfCfa::cfa_val_offset,
+ // 0x15 DW_CFA_val_offset_sf
+ &DwarfCfa::cfa_val_offset_sf,
+ // 0x16 DW_CFA_val_expression
+ &DwarfCfa::cfa_val_expression,
+ // 0x17 illegal cfa
+ nullptr,
+ // 0x18 illegal cfa
+ nullptr,
+ // 0x19 illegal cfa
+ nullptr,
+ // 0x1a illegal cfa
+ nullptr,
+ // 0x1b illegal cfa
+ nullptr,
+ // 0x1c DW_CFA_lo_user (Treat this as illegal)
+ nullptr,
+ // 0x1d illegal cfa
+ nullptr,
+ // 0x1e illegal cfa
+ nullptr,
+ // 0x1f illegal cfa
+ nullptr,
+ // 0x20 illegal cfa
+ nullptr,
+ // 0x21 illegal cfa
+ nullptr,
+ // 0x22 illegal cfa
+ nullptr,
+ // 0x23 illegal cfa
+ nullptr,
+ // 0x24 illegal cfa
+ nullptr,
+ // 0x25 illegal cfa
+ nullptr,
+ // 0x26 illegal cfa
+ nullptr,
+ // 0x27 illegal cfa
+ nullptr,
+ // 0x28 illegal cfa
+ nullptr,
+ // 0x29 illegal cfa
+ nullptr,
+ // 0x2a illegal cfa
+ nullptr,
+ // 0x2b illegal cfa
+ nullptr,
+ // 0x2c illegal cfa
+ nullptr,
+ // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
+ nullptr,
+ // 0x2e DW_CFA_GNU_args_size
+ &DwarfCfa::cfa_nop,
+ // 0x2f DW_CFA_GNU_negative_offset_extended
+ &DwarfCfa::cfa_gnu_negative_offset_extended,
+ // 0x30 illegal cfa
+ nullptr,
+ // 0x31 illegal cfa
+ nullptr,
+ // 0x32 illegal cfa
+ nullptr,
+ // 0x33 illegal cfa
+ nullptr,
+ // 0x34 illegal cfa
+ nullptr,
+ // 0x35 illegal cfa
+ nullptr,
+ // 0x36 illegal cfa
+ nullptr,
+ // 0x37 illegal cfa
+ nullptr,
+ // 0x38 illegal cfa
+ nullptr,
+ // 0x39 illegal cfa
+ nullptr,
+ // 0x3a illegal cfa
+ nullptr,
+ // 0x3b illegal cfa
+ nullptr,
+ // 0x3c illegal cfa
+ nullptr,
+ // 0x3d illegal cfa
+ nullptr,
+ // 0x3e illegal cfa
+ nullptr,
+ // 0x3f DW_CFA_hi_user (Treat this as illegal)
+ nullptr,
+ };
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
new file mode 100644
index 000000000..635cefdf2
--- /dev/null
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {
+ this->cie32_value_ = static_cast<uint32_t>(-1);
+ this->cie64_value_ = static_cast<uint64_t>(-1);
+ }
+ virtual ~DwarfDebugFrame() = default;
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+ return this->entries_offset_ + pointer;
+ }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+ return this->entries_offset_ + pointer;
+ }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
new file mode 100644
index 000000000..7a41e4550
--- /dev/null
+++ b/libunwindstack/DwarfEhFrame.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+ public:
+ DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfEhFrame() = default;
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+ return this->memory_.cur_offset() - pointer - 4;
+ }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+ return this->memory_.cur_offset() - pointer - 8;
+ }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override {
+ // The eh_frame uses relative pcs.
+ return pc + this->memory_.cur_offset() - 4;
+ }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
new file mode 100644
index 000000000..1358e51de
--- /dev/null
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
+
+namespace unwindstack {
+
+static inline bool IsEncodingRelative(uint8_t encoding) {
+ encoding >>= 4;
+ return encoding > 0 && encoding <= DW_EH_PE_funcrel;
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::EhFrameInit(uint64_t offset, uint64_t size,
+ int64_t section_bias) {
+ return DwarfSectionImpl<AddressType>::Init(offset, size, section_bias);
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t, int64_t section_bias) {
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_data_offset(offset);
+ memory_.set_cur_offset(offset);
+
+ hdr_section_bias_ = section_bias;
+
+ // Read the first four bytes all at once.
+ uint8_t data[4];
+ if (!memory_.ReadBytes(data, 4)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ version_ = data[0];
+ if (version_ != 1) {
+ // Unknown version.
+ last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ uint8_t ptr_encoding = data[1];
+ uint8_t fde_count_encoding = data[2];
+ table_encoding_ = data[3];
+ table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+ // If we can't perform a binary search on the entries, it's not worth
+ // using this object. The calling code will fall back to the DwarfEhFrame
+ // object in this case.
+ if (table_entry_size_ == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ uint64_t ptr_offset;
+ if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding, &ptr_offset)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ memory_.set_pc_offset(memory_.cur_offset());
+ if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (fde_count_ == 0) {
+ last_error_.code = DWARF_ERROR_NO_FDES;
+ return false;
+ }
+
+ hdr_entries_offset_ = memory_.cur_offset();
+ hdr_entries_data_offset_ = offset;
+
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+ uint64_t fde_offset;
+ if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+ return nullptr;
+ }
+ const DwarfFde* fde = this->GetFdeFromOffset(fde_offset);
+ if (fde == nullptr) {
+ return nullptr;
+ }
+
+ // There is a possibility that this entry points to a zero length FDE
+ // due to a bug. If this happens, try and find the non-zero length FDE
+ // from eh_frame directly. See b/142483624.
+ if (fde->pc_start == fde->pc_end) {
+ fde = DwarfSectionImpl<AddressType>::GetFdeFromPc(pc);
+ if (fde == nullptr) {
+ return nullptr;
+ }
+ }
+
+ // Guaranteed pc >= pc_start, need to check pc in the fde range.
+ if (pc < fde->pc_end) {
+ return fde;
+ }
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return nullptr;
+}
+
+template <typename AddressType>
+const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
+DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
+ auto entry = fde_info_.find(index);
+ if (entry != fde_info_.end()) {
+ return &fde_info_[index];
+ }
+ FdeInfo* info = &fde_info_[index];
+
+ memory_.set_data_offset(hdr_entries_data_offset_);
+ memory_.set_cur_offset(hdr_entries_offset_ + 2 * index * table_entry_size_);
+ memory_.set_pc_offset(0);
+ uint64_t value;
+ if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+ !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ fde_info_.erase(index);
+ return nullptr;
+ }
+
+ // Relative encodings require adding in the load bias.
+ if (IsEncodingRelative(table_encoding_)) {
+ value += hdr_section_bias_;
+ }
+ info->pc = value;
+ return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
+
+ size_t first = 0;
+ size_t last = fde_count_;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ const FdeInfo* info = GetFdeInfoFromIndex(current);
+ if (info == nullptr) {
+ return false;
+ }
+ if (pc == info->pc) {
+ *fde_offset = info->offset;
+ return true;
+ }
+ if (pc < info->pc) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ if (last != 0) {
+ const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+ if (info == nullptr) {
+ return false;
+ }
+ *fde_offset = info->offset;
+ return true;
+ }
+ return false;
+}
+
+template <typename AddressType>
+void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+ for (size_t i = 0; i < fde_count_; i++) {
+ const FdeInfo* info = GetFdeInfoFromIndex(i);
+ if (info == nullptr) {
+ break;
+ }
+ const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
+ if (fde == nullptr) {
+ break;
+ }
+
+ // There is a possibility that this entry points to a zero length FDE
+ // due to a bug. If this happens, try and find the non-zero length FDE
+ // from eh_frame directly. See b/142483624.
+ if (fde->pc_start == fde->pc_end) {
+ const DwarfFde* fde_real = DwarfSectionImpl<AddressType>::GetFdeFromPc(fde->pc_start);
+ if (fde_real != nullptr) {
+ fde = fde_real;
+ }
+ }
+ fdes->push_back(fde);
+ }
+}
+
+// Explicitly instantiate DwarfEhFrameWithHdr
+template class DwarfEhFrameWithHdr<uint32_t>;
+template class DwarfEhFrameWithHdr<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
new file mode 100644
index 000000000..f7c010ce4
--- /dev/null
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrameWithHdr : public DwarfSectionImpl<AddressType> {
+ public:
+ // Add these so that the protected members of DwarfSectionImpl
+ // can be accessed without needing a this->.
+ using DwarfSectionImpl<AddressType>::memory_;
+ using DwarfSectionImpl<AddressType>::last_error_;
+
+ struct FdeInfo {
+ AddressType pc;
+ uint64_t offset;
+ };
+
+ DwarfEhFrameWithHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+ virtual ~DwarfEhFrameWithHdr() = default;
+
+ uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+ return memory_.cur_offset() - pointer - 4;
+ }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+ return memory_.cur_offset() - pointer - 8;
+ }
+
+ uint64_t AdjustPcFromFde(uint64_t pc) override {
+ // The eh_frame uses relative pcs.
+ return pc + memory_.cur_offset() - 4;
+ }
+
+ bool EhFrameInit(uint64_t offset, uint64_t size, int64_t section_bias);
+ bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
+
+ const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+ bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset);
+
+ const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+ void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ protected:
+ uint8_t version_ = 0;
+ uint8_t table_encoding_ = 0;
+ size_t table_entry_size_ = 0;
+
+ uint64_t hdr_entries_offset_ = 0;
+ uint64_t hdr_entries_data_offset_ = 0;
+ uint64_t hdr_section_bias_ = 0;
+
+ uint64_t fde_count_ = 0;
+ std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 000000000..20db2221f
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum DwarfEncoding : uint8_t {
+ DW_EH_PE_omit = 0xff,
+
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0a,
+ DW_EH_PE_sdata4 = 0x0b,
+ DW_EH_PE_sdata8 = 0x0c,
+
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+
+ // The following are special values used to encode CFA and OP operands.
+ DW_EH_PE_udata1 = 0x0d,
+ DW_EH_PE_sdata1 = 0x0e,
+ DW_EH_PE_block = 0x0f,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 000000000..2e388c689
--- /dev/null
+++ b/libunwindstack/DwarfMemory.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <string>
+
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEncoding.h"
+
+namespace unwindstack {
+
+bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
+ if (!memory_->ReadFully(cur_offset_, dst, num_bytes)) {
+ return false;
+ }
+ cur_offset_ += num_bytes;
+ return true;
+}
+
+template <typename SignedType>
+bool DwarfMemory::ReadSigned(uint64_t* value) {
+ SignedType signed_value;
+ if (!ReadBytes(&signed_value, sizeof(SignedType))) {
+ return false;
+ }
+ *value = static_cast<int64_t>(signed_value);
+ return true;
+}
+
+bool DwarfMemory::ReadULEB128(uint64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ *value = cur_value;
+ return true;
+}
+
+bool DwarfMemory::ReadSLEB128(int64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ if (byte & 0x40) {
+ // Negative value, need to sign extend.
+ cur_value |= static_cast<uint64_t>(-1) << shift;
+ }
+ *value = static_cast<int64_t>(cur_value);
+ return true;
+}
+
+template <typename AddressType>
+size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ return sizeof(AddressType);
+ case DW_EH_PE_udata1:
+ case DW_EH_PE_sdata1:
+ return 1;
+ case DW_EH_PE_udata2:
+ case DW_EH_PE_sdata2:
+ return 2;
+ case DW_EH_PE_udata4:
+ case DW_EH_PE_sdata4:
+ return 4;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ return 8;
+ case DW_EH_PE_uleb128:
+ case DW_EH_PE_sleb128:
+ default:
+ return 0;
+ }
+}
+
+bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
+ CHECK((encoding & 0x0f) == 0);
+
+ // Handle the encoding.
+ switch (encoding) {
+ case DW_EH_PE_absptr:
+ // Nothing to do.
+ break;
+ case DW_EH_PE_pcrel:
+ if (pc_offset_ == INT64_MAX) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += pc_offset_;
+ break;
+ case DW_EH_PE_textrel:
+ if (text_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += text_offset_;
+ break;
+ case DW_EH_PE_datarel:
+ if (data_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += data_offset_;
+ break;
+ case DW_EH_PE_funcrel:
+ if (func_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += func_offset_;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
+ if (encoding == DW_EH_PE_omit) {
+ *value = 0;
+ return true;
+ } else if (encoding == DW_EH_PE_aligned) {
+ if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
+ return false;
+ }
+ cur_offset_ &= -sizeof(AddressType);
+
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ return ReadBytes(value, sizeof(AddressType));
+ }
+
+ // Get the data.
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ if (!ReadBytes(value, sizeof(AddressType))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_uleb128:
+ if (!ReadULEB128(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sleb128:
+ int64_t signed_value;
+ if (!ReadSLEB128(&signed_value)) {
+ return false;
+ }
+ *value = static_cast<uint64_t>(signed_value);
+ break;
+ case DW_EH_PE_udata1: {
+ uint8_t value8;
+ if (!ReadBytes(&value8, 1)) {
+ return false;
+ }
+ *value = value8;
+ } break;
+ case DW_EH_PE_sdata1:
+ if (!ReadSigned<int8_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata2: {
+ uint16_t value16;
+ if (!ReadBytes(&value16, 2)) {
+ return false;
+ }
+ *value = value16;
+ } break;
+ case DW_EH_PE_sdata2:
+ if (!ReadSigned<int16_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata4: {
+ uint32_t value32;
+ if (!ReadBytes(&value32, 4)) {
+ return false;
+ }
+ *value = value32;
+ } break;
+ case DW_EH_PE_sdata4:
+ if (!ReadSigned<int32_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata8:
+ if (!ReadBytes(value, sizeof(uint64_t))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sdata8:
+ if (!ReadSigned<int64_t>(value)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return AdjustEncodedValue(encoding & 0x70, value);
+}
+
+// Instantiate all of the needed template functions.
+template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
+
+template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
+template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
+
+template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
+template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 000000000..393eb3efc
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,1941 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfOp.h"
+
+namespace unwindstack {
+
+enum DwarfOpHandleFunc : uint8_t {
+ OP_ILLEGAL = 0,
+ OP_DEREF,
+ OP_DEREF_SIZE,
+ OP_PUSH,
+ OP_DUP,
+ OP_DROP,
+ OP_OVER,
+ OP_PICK,
+ OP_SWAP,
+ OP_ROT,
+ OP_ABS,
+ OP_AND,
+ OP_DIV,
+ OP_MINUS,
+ OP_MOD,
+ OP_MUL,
+ OP_NEG,
+ OP_NOT,
+ OP_OR,
+ OP_PLUS,
+ OP_PLUS_UCONST,
+ OP_SHL,
+ OP_SHR,
+ OP_SHRA,
+ OP_XOR,
+ OP_BRA,
+ OP_EQ,
+ OP_GE,
+ OP_GT,
+ OP_LE,
+ OP_LT,
+ OP_NE,
+ OP_SKIP,
+ OP_LIT,
+ OP_REG,
+ OP_REGX,
+ OP_BREG,
+ OP_BREGX,
+ OP_NOP,
+ OP_NOT_IMPLEMENTED,
+};
+
+struct OpCallback {
+ // It may seem tempting to "clean this up" by replacing "const char[26]" with
+ // "const char*", but doing so would place the entire callback table in
+ // .data.rel.ro section, instead of .rodata section, and thus increase
+ // dirty memory usage. Libunwindstack is used by the linker and therefore
+ // loaded for every running process, so every bit of memory counts.
+ // Unlike C standard, C++ standard guarantees this array is big enough to
+ // store the names, or else we would get a compilation error.
+ const char name[26];
+
+ // Similarily for this field, we do NOT want to directly store function
+ // pointers here. Not only would that cause the callback table to be placed
+ // in .data.rel.ro section, but it would be duplicated for each AddressType.
+ // Instead, we use DwarfOpHandleFunc enum to decouple the callback table from
+ // the function pointers.
+ DwarfOpHandleFunc handle_func;
+
+ uint8_t num_required_stack_values;
+ uint8_t num_operands;
+ uint8_t operands[2];
+};
+
+constexpr static OpCallback kCallbackTable[256] = {
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x00 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x01 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x02 illegal op
+ {
+ // 0x03 DW_OP_addr
+ "DW_OP_addr",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_absptr},
+ },
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x04 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x05 illegal op
+ {
+ // 0x06 DW_OP_deref
+ "DW_OP_deref",
+ OP_DEREF,
+ 1,
+ 0,
+ {},
+ },
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x07 illegal op
+ {
+ // 0x08 DW_OP_const1u
+ "DW_OP_const1u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x09 DW_OP_const1s
+ "DW_OP_const1s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata1},
+ },
+ {
+ // 0x0a DW_OP_const2u
+ "DW_OP_const2u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x0b DW_OP_const2s
+ "DW_OP_const2s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x0c DW_OP_const4u
+ "DW_OP_const4u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x0d DW_OP_const4s
+ "DW_OP_const4s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata4},
+ },
+ {
+ // 0x0e DW_OP_const8u
+ "DW_OP_const8u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata8},
+ },
+ {
+ // 0x0f DW_OP_const8s
+ "DW_OP_const8s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata8},
+ },
+ {
+ // 0x10 DW_OP_constu
+ "DW_OP_constu",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x11 DW_OP_consts
+ "DW_OP_consts",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x12 DW_OP_dup
+ "DW_OP_dup",
+ OP_DUP,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x13 DW_OP_drop
+ "DW_OP_drop",
+ OP_DROP,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x14 DW_OP_over
+ "DW_OP_over",
+ OP_OVER,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x15 DW_OP_pick
+ "DW_OP_pick",
+ OP_PICK,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x16 DW_OP_swap
+ "DW_OP_swap",
+ OP_SWAP,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x17 DW_OP_rot
+ "DW_OP_rot",
+ OP_ROT,
+ 3,
+ 0,
+ {},
+ },
+ {
+ // 0x18 DW_OP_xderef
+ "DW_OP_xderef",
+ OP_NOT_IMPLEMENTED,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x19 DW_OP_abs
+ "DW_OP_abs",
+ OP_ABS,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x1a DW_OP_and
+ "DW_OP_and",
+ OP_AND,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1b DW_OP_div
+ "DW_OP_div",
+ OP_DIV,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1c DW_OP_minus
+ "DW_OP_minus",
+ OP_MINUS,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1d DW_OP_mod
+ "DW_OP_mod",
+ OP_MOD,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1e DW_OP_mul
+ "DW_OP_mul",
+ OP_MUL,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1f DW_OP_neg
+ "DW_OP_neg",
+ OP_NEG,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x20 DW_OP_not
+ "DW_OP_not",
+ OP_NOT,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x21 DW_OP_or
+ "DW_OP_or",
+ OP_OR,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x22 DW_OP_plus
+ "DW_OP_plus",
+ OP_PLUS,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x23 DW_OP_plus_uconst
+ "DW_OP_plus_uconst",
+ OP_PLUS_UCONST,
+ 1,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x24 DW_OP_shl
+ "DW_OP_shl",
+ OP_SHL,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x25 DW_OP_shr
+ "DW_OP_shr",
+ OP_SHR,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x26 DW_OP_shra
+ "DW_OP_shra",
+ OP_SHRA,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x27 DW_OP_xor
+ "DW_OP_xor",
+ OP_XOR,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x28 DW_OP_bra
+ "DW_OP_bra",
+ OP_BRA,
+ 1,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x29 DW_OP_eq
+ "DW_OP_eq",
+ OP_EQ,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2a DW_OP_ge
+ "DW_OP_ge",
+ OP_GE,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2b DW_OP_gt
+ "DW_OP_gt",
+ OP_GT,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2c DW_OP_le
+ "DW_OP_le",
+ OP_LE,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2d DW_OP_lt
+ "DW_OP_lt",
+ OP_LT,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2e DW_OP_ne
+ "DW_OP_ne",
+ OP_NE,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2f DW_OP_skip
+ "DW_OP_skip",
+ OP_SKIP,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x30 DW_OP_lit0
+ "DW_OP_lit0",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x31 DW_OP_lit1
+ "DW_OP_lit1",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x32 DW_OP_lit2
+ "DW_OP_lit2",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x33 DW_OP_lit3
+ "DW_OP_lit3",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x34 DW_OP_lit4
+ "DW_OP_lit4",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x35 DW_OP_lit5
+ "DW_OP_lit5",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x36 DW_OP_lit6
+ "DW_OP_lit6",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x37 DW_OP_lit7
+ "DW_OP_lit7",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x38 DW_OP_lit8
+ "DW_OP_lit8",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x39 DW_OP_lit9
+ "DW_OP_lit9",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3a DW_OP_lit10
+ "DW_OP_lit10",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3b DW_OP_lit11
+ "DW_OP_lit11",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3c DW_OP_lit12
+ "DW_OP_lit12",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3d DW_OP_lit13
+ "DW_OP_lit13",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3e DW_OP_lit14
+ "DW_OP_lit14",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3f DW_OP_lit15
+ "DW_OP_lit15",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x40 DW_OP_lit16
+ "DW_OP_lit16",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x41 DW_OP_lit17
+ "DW_OP_lit17",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x42 DW_OP_lit18
+ "DW_OP_lit18",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x43 DW_OP_lit19
+ "DW_OP_lit19",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x44 DW_OP_lit20
+ "DW_OP_lit20",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x45 DW_OP_lit21
+ "DW_OP_lit21",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x46 DW_OP_lit22
+ "DW_OP_lit22",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x47 DW_OP_lit23
+ "DW_OP_lit23",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x48 DW_OP_lit24
+ "DW_OP_lit24",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x49 DW_OP_lit25
+ "DW_OP_lit25",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4a DW_OP_lit26
+ "DW_OP_lit26",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4b DW_OP_lit27
+ "DW_OP_lit27",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4c DW_OP_lit28
+ "DW_OP_lit28",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4d DW_OP_lit29
+ "DW_OP_lit29",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4e DW_OP_lit30
+ "DW_OP_lit30",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4f DW_OP_lit31
+ "DW_OP_lit31",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x50 DW_OP_reg0
+ "DW_OP_reg0",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x51 DW_OP_reg1
+ "DW_OP_reg1",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x52 DW_OP_reg2
+ "DW_OP_reg2",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x53 DW_OP_reg3
+ "DW_OP_reg3",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x54 DW_OP_reg4
+ "DW_OP_reg4",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x55 DW_OP_reg5
+ "DW_OP_reg5",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x56 DW_OP_reg6
+ "DW_OP_reg6",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x57 DW_OP_reg7
+ "DW_OP_reg7",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x58 DW_OP_reg8
+ "DW_OP_reg8",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x59 DW_OP_reg9
+ "DW_OP_reg9",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5a DW_OP_reg10
+ "DW_OP_reg10",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5b DW_OP_reg11
+ "DW_OP_reg11",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5c DW_OP_reg12
+ "DW_OP_reg12",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5d DW_OP_reg13
+ "DW_OP_reg13",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5e DW_OP_reg14
+ "DW_OP_reg14",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5f DW_OP_reg15
+ "DW_OP_reg15",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x60 DW_OP_reg16
+ "DW_OP_reg16",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x61 DW_OP_reg17
+ "DW_OP_reg17",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x62 DW_OP_reg18
+ "DW_OP_reg18",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x63 DW_OP_reg19
+ "DW_OP_reg19",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x64 DW_OP_reg20
+ "DW_OP_reg20",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x65 DW_OP_reg21
+ "DW_OP_reg21",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x66 DW_OP_reg22
+ "DW_OP_reg22",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x67 DW_OP_reg23
+ "DW_OP_reg23",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x68 DW_OP_reg24
+ "DW_OP_reg24",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x69 DW_OP_reg25
+ "DW_OP_reg25",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6a DW_OP_reg26
+ "DW_OP_reg26",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6b DW_OP_reg27
+ "DW_OP_reg27",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6c DW_OP_reg28
+ "DW_OP_reg28",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6d DW_OP_reg29
+ "DW_OP_reg29",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6e DW_OP_reg30
+ "DW_OP_reg30",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6f DW_OP_reg31
+ "DW_OP_reg31",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x70 DW_OP_breg0
+ "DW_OP_breg0",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x71 DW_OP_breg1
+ "DW_OP_breg1",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x72 DW_OP_breg2
+ "DW_OP_breg2",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x73 DW_OP_breg3
+ "DW_OP_breg3",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x74 DW_OP_breg4
+ "DW_OP_breg4",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x75 DW_OP_breg5
+ "DW_OP_breg5",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x76 DW_OP_breg6
+ "DW_OP_breg6",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x77 DW_OP_breg7
+ "DW_OP_breg7",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x78 DW_OP_breg8
+ "DW_OP_breg8",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x79 DW_OP_breg9
+ "DW_OP_breg9",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7a DW_OP_breg10
+ "DW_OP_breg10",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7b DW_OP_breg11
+ "DW_OP_breg11",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7c DW_OP_breg12
+ "DW_OP_breg12",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7d DW_OP_breg13
+ "DW_OP_breg13",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7e DW_OP_breg14
+ "DW_OP_breg14",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7f DW_OP_breg15
+ "DW_OP_breg15",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x80 DW_OP_breg16
+ "DW_OP_breg16",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x81 DW_OP_breg17
+ "DW_OP_breg17",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x82 DW_OP_breg18
+ "DW_OP_breg18",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x83 DW_OP_breg19
+ "DW_OP_breg19",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x84 DW_OP_breg20
+ "DW_OP_breg20",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x85 DW_OP_breg21
+ "DW_OP_breg21",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x86 DW_OP_breg22
+ "DW_OP_breg22",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x87 DW_OP_breg23
+ "DW_OP_breg23",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x88 DW_OP_breg24
+ "DW_OP_breg24",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x89 DW_OP_breg25
+ "DW_OP_breg25",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8a DW_OP_breg26
+ "DW_OP_breg26",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8b DW_OP_breg27
+ "DW_OP_breg27",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8c DW_OP_breg28
+ "DW_OP_breg28",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8d DW_OP_breg29
+ "DW_OP_breg29",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8e DW_OP_breg30
+ "DW_OP_breg30",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8f DW_OP_breg31
+ "DW_OP_breg31",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x90 DW_OP_regx
+ "DW_OP_regx",
+ OP_REGX,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x91 DW_OP_fbreg
+ "DW_OP_fbreg",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x92 DW_OP_bregx
+ "DW_OP_bregx",
+ OP_BREGX,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ },
+ {
+ // 0x93 DW_OP_piece
+ "DW_OP_piece",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x94 DW_OP_deref_size
+ "DW_OP_deref_size",
+ OP_DEREF_SIZE,
+ 1,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x95 DW_OP_xderef_size
+ "DW_OP_xderef_size",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x96 DW_OP_nop
+ "DW_OP_nop",
+ OP_NOP,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x97 DW_OP_push_object_address
+ "DW_OP_push_object_address",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x98 DW_OP_call2
+ "DW_OP_call2",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x99 DW_OP_call4
+ "DW_OP_call4",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x9a DW_OP_call_ref
+ "DW_OP_call_ref",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0, // Has a different sized operand (4 bytes or 8 bytes).
+ {},
+ },
+ {
+ // 0x9b DW_OP_form_tls_address
+ "DW_OP_form_tls_address",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9c DW_OP_call_frame_cfa
+ "DW_OP_call_frame_cfa",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9d DW_OP_bit_piece
+ "DW_OP_bit_piece",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9e DW_OP_implicit_value
+ "DW_OP_implicit_value",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9f DW_OP_stack_value
+ "DW_OP_stack_value",
+ OP_NOT_IMPLEMENTED,
+ 1,
+ 0,
+ {},
+ },
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xaa illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xab illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xac illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xad illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xae illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xaf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xba illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbe illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xca illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xce illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xda illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xde illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe0 DW_OP_lo_user
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xea illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xeb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xec illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xed illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xee illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xef illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfa illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfe illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xff DW_OP_hi_user
+};
+
+template <typename AddressType>
+const typename DwarfOp<AddressType>::OpHandleFuncPtr DwarfOp<AddressType>::kOpHandleFuncList[] = {
+ [OP_ILLEGAL] = nullptr,
+ [OP_DEREF] = &DwarfOp<AddressType>::op_deref,
+ [OP_DEREF_SIZE] = &DwarfOp<AddressType>::op_deref_size,
+ [OP_PUSH] = &DwarfOp<AddressType>::op_push,
+ [OP_DUP] = &DwarfOp<AddressType>::op_dup,
+ [OP_DROP] = &DwarfOp<AddressType>::op_drop,
+ [OP_OVER] = &DwarfOp<AddressType>::op_over,
+ [OP_PICK] = &DwarfOp<AddressType>::op_pick,
+ [OP_SWAP] = &DwarfOp<AddressType>::op_swap,
+ [OP_ROT] = &DwarfOp<AddressType>::op_rot,
+ [OP_ABS] = &DwarfOp<AddressType>::op_abs,
+ [OP_AND] = &DwarfOp<AddressType>::op_and,
+ [OP_DIV] = &DwarfOp<AddressType>::op_div,
+ [OP_MINUS] = &DwarfOp<AddressType>::op_minus,
+ [OP_MOD] = &DwarfOp<AddressType>::op_mod,
+ [OP_MUL] = &DwarfOp<AddressType>::op_mul,
+ [OP_NEG] = &DwarfOp<AddressType>::op_neg,
+ [OP_NOT] = &DwarfOp<AddressType>::op_not,
+ [OP_OR] = &DwarfOp<AddressType>::op_or,
+ [OP_PLUS] = &DwarfOp<AddressType>::op_plus,
+ [OP_PLUS_UCONST] = &DwarfOp<AddressType>::op_plus_uconst,
+ [OP_SHL] = &DwarfOp<AddressType>::op_shl,
+ [OP_SHR] = &DwarfOp<AddressType>::op_shr,
+ [OP_SHRA] = &DwarfOp<AddressType>::op_shra,
+ [OP_XOR] = &DwarfOp<AddressType>::op_xor,
+ [OP_BRA] = &DwarfOp<AddressType>::op_bra,
+ [OP_EQ] = &DwarfOp<AddressType>::op_eq,
+ [OP_GE] = &DwarfOp<AddressType>::op_ge,
+ [OP_GT] = &DwarfOp<AddressType>::op_gt,
+ [OP_LE] = &DwarfOp<AddressType>::op_le,
+ [OP_LT] = &DwarfOp<AddressType>::op_lt,
+ [OP_NE] = &DwarfOp<AddressType>::op_ne,
+ [OP_SKIP] = &DwarfOp<AddressType>::op_skip,
+ [OP_LIT] = &DwarfOp<AddressType>::op_lit,
+ [OP_REG] = &DwarfOp<AddressType>::op_reg,
+ [OP_REGX] = &DwarfOp<AddressType>::op_regx,
+ [OP_BREG] = &DwarfOp<AddressType>::op_breg,
+ [OP_BREGX] = &DwarfOp<AddressType>::op_bregx,
+ [OP_NOP] = &DwarfOp<AddressType>::op_nop,
+ [OP_NOT_IMPLEMENTED] = &DwarfOp<AddressType>::op_not_implemented,
+};
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
+ is_register_ = false;
+ stack_.clear();
+ memory_->set_cur_offset(start);
+ dex_pc_set_ = false;
+
+ // Unroll the first Decode calls to be able to check for a special
+ // sequence of ops and values that indicate this is the dex pc.
+ // The pattern is:
+ // OP_const4u (0x0c) 'D' 'E' 'X' '1'
+ // OP_drop (0x13)
+ if (memory_->cur_offset() < end) {
+ if (!Decode()) {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ bool check_for_drop;
+ if (cur_op_ == 0x0c && operands_.back() == 0x31584544) {
+ check_for_drop = true;
+ } else {
+ check_for_drop = false;
+ }
+ if (memory_->cur_offset() < end) {
+ if (!Decode()) {
+ return false;
+ }
+ } else {
+ return true;
+ }
+
+ if (check_for_drop && cur_op_ == 0x13) {
+ dex_pc_set_ = true;
+ }
+
+ uint32_t iterations = 2;
+ while (memory_->cur_offset() < end) {
+ if (!Decode()) {
+ return false;
+ }
+ // To protect against a branch that creates an infinite loop,
+ // terminate if the number of iterations gets too high.
+ if (iterations++ == 1000) {
+ last_error_.code = DWARF_ERROR_TOO_MANY_ITERATIONS;
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Decode() {
+ last_error_.code = DWARF_ERROR_NONE;
+ if (!memory_->ReadBytes(&cur_op_, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_->cur_offset();
+ return false;
+ }
+
+ const auto* op = &kCallbackTable[cur_op_];
+ if (op->handle_func == OP_ILLEGAL) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ const auto handle_func = kOpHandleFuncList[op->handle_func];
+
+ // Make sure that the required number of stack elements is available.
+ if (stack_.size() < op->num_required_stack_values) {
+ last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+
+ operands_.clear();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_->cur_offset();
+ return false;
+ }
+ operands_.push_back(value);
+ }
+ return (this->*handle_func)();
+}
+
+template <typename AddressType>
+void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end,
+ std::vector<std::string>* lines) {
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ uint8_t cur_op;
+ if (!memory_->ReadBytes(&cur_op, 1)) {
+ return;
+ }
+
+ std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
+ std::string log_string;
+ const auto* op = &kCallbackTable[cur_op];
+ if (op->handle_func == OP_ILLEGAL) {
+ log_string = "Illegal";
+ } else {
+ log_string = op->name;
+ uint64_t start_offset = memory_->cur_offset();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ return;
+ }
+ log_string += ' ' + std::to_string(value);
+ }
+ uint64_t end_offset = memory_->cur_offset();
+
+ memory_->set_cur_offset(start_offset);
+ for (size_t i = start_offset; i < end_offset; i++) {
+ uint8_t byte;
+ if (!memory_->ReadBytes(&byte, 1)) {
+ return;
+ }
+ raw_string += android::base::StringPrintf(" 0x%02x", byte);
+ }
+ memory_->set_cur_offset(end_offset);
+ }
+ lines->push_back(std::move(log_string));
+ lines->push_back(std::move(raw_string));
+ }
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref() {
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value;
+ if (!regular_memory()->ReadFully(addr, &value, sizeof(value))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = addr;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref_size() {
+ AddressType bytes_to_read = OperandAt(0);
+ if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value = 0;
+ if (!regular_memory()->ReadFully(addr, &value, bytes_to_read)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = addr;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_push() {
+ // Push all of the operands.
+ for (auto operand : operands_) {
+ stack_.push_front(operand);
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_dup() {
+ stack_.push_front(StackAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_drop() {
+ StackPop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_over() {
+ stack_.push_front(StackAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_pick() {
+ AddressType index = OperandAt(0);
+ if (index > StackSize()) {
+ last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+ stack_.push_front(StackAt(index));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_swap() {
+ AddressType old_value = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = old_value;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_rot() {
+ AddressType top = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = stack_[2];
+ stack_[2] = top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_abs() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ if (signed_value < 0) {
+ signed_value = -signed_value;
+ }
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_and() {
+ AddressType top = StackPop();
+ stack_[0] &= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_div() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ SignedType signed_divisor = static_cast<SignedType>(top);
+ SignedType signed_dividend = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_minus() {
+ AddressType top = StackPop();
+ stack_[0] -= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mod() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_[0] %= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mul() {
+ AddressType top = StackPop();
+ stack_[0] *= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_neg() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(-signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not() {
+ stack_[0] = ~stack_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_or() {
+ AddressType top = StackPop();
+ stack_[0] |= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus() {
+ AddressType top = StackPop();
+ stack_[0] += top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus_uconst() {
+ stack_[0] += OperandAt(0);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shl() {
+ AddressType top = StackPop();
+ stack_[0] <<= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shr() {
+ AddressType top = StackPop();
+ stack_[0] >>= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shra() {
+ AddressType top = StackPop();
+ SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top;
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_xor() {
+ AddressType top = StackPop();
+ stack_[0] ^= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bra() {
+ // Requires one stack element.
+ AddressType top = StackPop();
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset;
+ if (top != 0) {
+ cur_offset = memory_->cur_offset() + offset;
+ } else {
+ cur_offset = memory_->cur_offset() - offset;
+ }
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_eq() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] == top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ge() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] >= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_gt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] > top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_le() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] <= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] < top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ne() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] != top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_skip() {
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset = memory_->cur_offset() + offset;
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lit() {
+ stack_.push_front(cur_op() - 0x30);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_reg() {
+ is_register_ = true;
+ stack_.push_front(cur_op() - 0x50);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_regx() {
+ is_register_ = true;
+ stack_.push_front(OperandAt(0));
+ return true;
+}
+
+// It's not clear for breg/bregx, if this op should read the current
+// value of the register, or where we think that register is located.
+// For simplicity, the code will read the value before doing the unwind.
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_breg() {
+ uint16_t reg = cur_op() - 0x70;
+ if (reg >= regs_info_->Total()) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front(regs_info_->Get(reg) + OperandAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bregx() {
+ AddressType reg = OperandAt(0);
+ if (reg >= regs_info_->Total()) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front(regs_info_->Get(reg) + OperandAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_nop() {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not_implemented() {
+ last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+}
+
+// Explicitly instantiate DwarfOp.
+template class DwarfOp<uint32_t>;
+template class DwarfOp<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 000000000..ac9fd2d16
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_OP_H
+#define _LIBUNWINDSTACK_DWARF_OP_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <unwindstack/DwarfError.h>
+
+#include "DwarfEncoding.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
+
+// Forward declarations.
+class DwarfMemory;
+class Memory;
+template <typename AddressType>
+class RegsImpl;
+
+template <typename AddressType>
+class DwarfOp {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ public:
+ DwarfOp(DwarfMemory* memory, Memory* regular_memory)
+ : memory_(memory), regular_memory_(regular_memory) {}
+ virtual ~DwarfOp() = default;
+
+ bool Decode();
+
+ bool Eval(uint64_t start, uint64_t end);
+
+ void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
+
+ AddressType StackAt(size_t index) { return stack_[index]; }
+ size_t StackSize() { return stack_.size(); }
+
+ void set_regs_info(RegsInfo<AddressType>* regs_info) { regs_info_ = regs_info; }
+
+ const DwarfErrorData& last_error() { return last_error_; }
+ DwarfErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ bool dex_pc_set() { return dex_pc_set_; }
+
+ bool is_register() { return is_register_; }
+
+ uint8_t cur_op() { return cur_op_; }
+
+ Memory* regular_memory() { return regular_memory_; }
+
+ protected:
+ AddressType OperandAt(size_t index) { return operands_[index]; }
+ size_t OperandsSize() { return operands_.size(); }
+
+ AddressType StackPop() {
+ AddressType value = stack_.front();
+ stack_.pop_front();
+ return value;
+ }
+
+ private:
+ DwarfMemory* memory_;
+ Memory* regular_memory_;
+
+ RegsInfo<AddressType>* regs_info_;
+ bool dex_pc_set_ = false;
+ bool is_register_ = false;
+ DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
+ uint8_t cur_op_;
+ std::vector<AddressType> operands_;
+ std::deque<AddressType> stack_;
+
+ inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
+
+ // Op processing functions.
+ bool op_deref();
+ bool op_deref_size();
+ bool op_push();
+ bool op_dup();
+ bool op_drop();
+ bool op_over();
+ bool op_pick();
+ bool op_swap();
+ bool op_rot();
+ bool op_abs();
+ bool op_and();
+ bool op_div();
+ bool op_minus();
+ bool op_mod();
+ bool op_mul();
+ bool op_neg();
+ bool op_not();
+ bool op_or();
+ bool op_plus();
+ bool op_plus_uconst();
+ bool op_shl();
+ bool op_shr();
+ bool op_shra();
+ bool op_xor();
+ bool op_bra();
+ bool op_eq();
+ bool op_ge();
+ bool op_gt();
+ bool op_le();
+ bool op_lt();
+ bool op_ne();
+ bool op_skip();
+ bool op_lit();
+ bool op_reg();
+ bool op_regx();
+ bool op_breg();
+ bool op_bregx();
+ bool op_nop();
+ bool op_not_implemented();
+
+ using OpHandleFuncPtr = bool (DwarfOp::*)();
+ static const OpHandleFuncPtr kOpHandleFuncList[];
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
new file mode 100644
index 000000000..18bd490f9
--- /dev/null
+++ b/libunwindstack/DwarfSection.cpp
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfCfa.h"
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+#include "DwarfOp.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
+
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
+
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+ // Lookup the pc in the cache.
+ auto it = loc_regs_.upper_bound(pc);
+ if (it == loc_regs_.end() || pc < it->second.pc_start) {
+ last_error_.code = DWARF_ERROR_NONE;
+ const DwarfFde* fde = GetFdeFromPc(pc);
+ if (fde == nullptr || fde->cie == nullptr) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ // Now get the location information for this pc.
+ dwarf_loc_regs_t loc_regs;
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ return false;
+ }
+ loc_regs.cie = fde->cie;
+
+ // Store it in the cache.
+ it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
+ }
+
+ // Now eval the actual registers.
+ return Eval(it->second.cie, process_memory, it->second, regs, finished);
+}
+
+template <typename AddressType>
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCieFromOffset(uint64_t offset) {
+ auto cie_entry = cie_entries_.find(offset);
+ if (cie_entry != cie_entries_.end()) {
+ return &cie_entry->second;
+ }
+ DwarfCie* cie = &cie_entries_[offset];
+ memory_.set_data_offset(entries_offset_);
+ memory_.set_cur_offset(offset);
+ if (!FillInCieHeader(cie) || !FillInCie(cie)) {
+ // Erase the cached entry.
+ cie_entries_.erase(offset);
+ return nullptr;
+ }
+ return cie;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCieHeader(DwarfCie* cie) {
+ cie->lsda_encoding = DW_EH_PE_omit;
+ uint32_t length32;
+ if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ if (length32 == static_cast<uint32_t>(-1)) {
+ // 64 bit Cie
+ uint64_t length64;
+ if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ cie->cfa_instructions_end = memory_.cur_offset() + length64;
+ cie->fde_address_encoding = DW_EH_PE_sdata8;
+
+ uint64_t cie_id;
+ if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ if (cie_id != cie64_value_) {
+ // This is not a Cie, something has gone horribly wrong.
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ } else {
+ // 32 bit Cie
+ cie->cfa_instructions_end = memory_.cur_offset() + length32;
+ cie->fde_address_encoding = DW_EH_PE_sdata4;
+
+ uint32_t cie_id;
+ if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ if (cie_id != cie32_value_) {
+ // This is not a Cie, something has gone horribly wrong.
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+ if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
+ // Unrecognized version.
+ last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
+ return false;
+ }
+
+ // Read the augmentation string.
+ char aug_value;
+ do {
+ if (!memory_.ReadBytes(&aug_value, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ cie->augmentation_string.push_back(aug_value);
+ } while (aug_value != '\0');
+
+ if (cie->version == 4 || cie->version == 5) {
+ // Skip the Address Size field since we only use it for validation.
+ memory_.set_cur_offset(memory_.cur_offset() + 1);
+
+ // Segment Size
+ if (!memory_.ReadBytes(&cie->segment_size, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ }
+
+ // Code Alignment Factor
+ if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ // Data Alignment Factor
+ if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (cie->version == 1) {
+ // Return Address is a single byte.
+ uint8_t return_address_register;
+ if (!memory_.ReadBytes(&return_address_register, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ cie->return_address_register = return_address_register;
+ } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (cie->augmentation_string[0] != 'z') {
+ cie->cfa_instructions_offset = memory_.cur_offset();
+ return true;
+ }
+
+ uint64_t aug_length;
+ if (!memory_.ReadULEB128(&aug_length)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
+
+ for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
+ switch (cie->augmentation_string[i]) {
+ case 'L':
+ if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ break;
+ case 'P': {
+ uint8_t encoding;
+ if (!memory_.ReadBytes(&encoding, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ memory_.set_pc_offset(pc_offset_);
+ if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ } break;
+ case 'R':
+ if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
+ auto fde_entry = fde_entries_.find(offset);
+ if (fde_entry != fde_entries_.end()) {
+ return &fde_entry->second;
+ }
+ DwarfFde* fde = &fde_entries_[offset];
+ memory_.set_data_offset(entries_offset_);
+ memory_.set_cur_offset(offset);
+ if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
+ fde_entries_.erase(offset);
+ return nullptr;
+ }
+ return fde;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFdeHeader(DwarfFde* fde) {
+ uint32_t length32;
+ if (!memory_.ReadBytes(&length32, sizeof(length32))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (length32 == static_cast<uint32_t>(-1)) {
+ // 64 bit Fde.
+ uint64_t length64;
+ if (!memory_.ReadBytes(&length64, sizeof(length64))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ fde->cfa_instructions_end = memory_.cur_offset() + length64;
+
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ if (value64 == cie64_value_) {
+ // This is a Cie, this means something has gone wrong.
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the Cie pointer, which is necessary to properly read the rest of
+ // of the Fde information.
+ fde->cie_offset = GetCieOffsetFromFde64(value64);
+ } else {
+ // 32 bit Fde.
+ fde->cfa_instructions_end = memory_.cur_offset() + length32;
+
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ if (value32 == cie32_value_) {
+ // This is a Cie, this means something has gone wrong.
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the Cie pointer, which is necessary to properly read the rest of
+ // of the Fde information.
+ fde->cie_offset = GetCieOffsetFromFde32(value32);
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+ uint64_t cur_offset = memory_.cur_offset();
+
+ const DwarfCie* cie = GetCieFromOffset(fde->cie_offset);
+ if (cie == nullptr) {
+ return false;
+ }
+ fde->cie = cie;
+
+ if (cie->segment_size != 0) {
+ // Skip over the segment selector for now.
+ cur_offset += cie->segment_size;
+ }
+ memory_.set_cur_offset(cur_offset);
+
+ // The load bias only applies to the start.
+ memory_.set_pc_offset(section_bias_);
+ bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
+ fde->pc_start = AdjustPcFromFde(fde->pc_start);
+
+ memory_.set_pc_offset(0);
+ if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ fde->pc_end += fde->pc_start;
+
+ if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
+ // Augmentation Size
+ uint64_t aug_length;
+ if (!memory_.ReadULEB128(&aug_length)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ uint64_t cur_offset = memory_.cur_offset();
+
+ memory_.set_pc_offset(pc_offset_);
+ if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ // Set our position to after all of the augmentation data.
+ memory_.set_cur_offset(cur_offset + aug_length);
+ }
+ fde->cfa_instructions_offset = memory_.cur_offset();
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
+ AddressType* value,
+ RegsInfo<AddressType>* regs_info,
+ bool* is_dex_pc) {
+ DwarfOp<AddressType> op(&memory_, regular_memory);
+ op.set_regs_info(regs_info);
+
+ // Need to evaluate the op data.
+ uint64_t end = loc.values[1];
+ uint64_t start = end - loc.values[0];
+ if (!op.Eval(start, end)) {
+ last_error_ = op.last_error();
+ return false;
+ }
+ if (op.StackSize() == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ // We don't support an expression that evaluates to a register number.
+ if (op.is_register()) {
+ last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+ }
+ *value = op.StackAt(0);
+ if (is_dex_pc != nullptr && op.dex_pc_set()) {
+ *is_dex_pc = true;
+ }
+ return true;
+}
+
+template <typename AddressType>
+struct EvalInfo {
+ const dwarf_loc_regs_t* loc_regs;
+ const DwarfCie* cie;
+ Memory* regular_memory;
+ AddressType cfa;
+ bool return_address_undefined = false;
+ RegsInfo<AddressType> regs_info;
+};
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint32_t reg,
+ AddressType* reg_ptr, void* info) {
+ EvalInfo<AddressType>* eval_info = reinterpret_cast<EvalInfo<AddressType>*>(info);
+ Memory* regular_memory = eval_info->regular_memory;
+ switch (loc->type) {
+ case DWARF_LOCATION_OFFSET:
+ if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = eval_info->cfa + loc->values[0];
+ return false;
+ }
+ break;
+ case DWARF_LOCATION_VAL_OFFSET:
+ *reg_ptr = eval_info->cfa + loc->values[0];
+ break;
+ case DWARF_LOCATION_REGISTER: {
+ uint32_t cur_reg = loc->values[0];
+ if (cur_reg >= eval_info->regs_info.Total()) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ *reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
+ break;
+ }
+ case DWARF_LOCATION_EXPRESSION:
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ bool is_dex_pc = false;
+ if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
+ return false;
+ }
+ if (loc->type == DWARF_LOCATION_EXPRESSION) {
+ if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = value;
+ return false;
+ }
+ } else {
+ *reg_ptr = value;
+ if (is_dex_pc) {
+ eval_info->regs_info.regs->set_dex_pc(value);
+ }
+ }
+ break;
+ }
+ case DWARF_LOCATION_UNDEFINED:
+ if (reg == eval_info->cie->return_address_register) {
+ eval_info->return_address_undefined = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+ const dwarf_loc_regs_t& loc_regs, Regs* regs,
+ bool* finished) {
+ RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+ if (cie->return_address_register >= cur_regs->total_regs()) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Get the cfa value;
+ auto cfa_entry = loc_regs.find(CFA_REG);
+ if (cfa_entry == loc_regs.end()) {
+ last_error_.code = DWARF_ERROR_CFA_NOT_DEFINED;
+ return false;
+ }
+
+ // Always set the dex pc to zero when evaluating.
+ cur_regs->set_dex_pc(0);
+
+ EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
+ .cie = cie,
+ .regular_memory = regular_memory,
+ .regs_info = RegsInfo<AddressType>(cur_regs)};
+ const DwarfLocation* loc = &cfa_entry->second;
+ // Only a few location types are valid for the cfa.
+ switch (loc->type) {
+ case DWARF_LOCATION_REGISTER:
+ if (loc->values[0] >= cur_regs->total_regs()) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ eval_info.cfa = (*cur_regs)[loc->values[0]];
+ eval_info.cfa += loc->values[1];
+ break;
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
+ return false;
+ }
+ // There is only one type of valid expression for CFA evaluation.
+ eval_info.cfa = value;
+ break;
+ }
+ default:
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ for (const auto& entry : loc_regs) {
+ uint32_t reg = entry.first;
+ // Already handled the CFA register.
+ if (reg == CFA_REG) continue;
+
+ AddressType* reg_ptr;
+ if (reg >= cur_regs->total_regs()) {
+ // Skip this unknown register.
+ continue;
+ }
+
+ reg_ptr = eval_info.regs_info.Save(reg);
+ if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
+ return false;
+ }
+ }
+
+ // Find the return address location.
+ if (eval_info.return_address_undefined) {
+ cur_regs->set_pc(0);
+ } else {
+ cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+ }
+
+ // If the pc was set to zero, consider this the final frame.
+ *finished = (cur_regs->pc() == 0) ? true : false;
+
+ cur_regs->set_sp(eval_info.cfa);
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
+ dwarf_loc_regs_t* loc_regs) {
+ DwarfCfa<AddressType> cfa(&memory_, fde);
+
+ // Look for the cached copy of the cie data.
+ auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
+ if (reg_entry == cie_loc_regs_.end()) {
+ if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
+ loc_regs)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ cie_loc_regs_[fde->cie_offset] = *loc_regs;
+ }
+ cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
+ if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
+ DwarfCfa<AddressType> cfa(&memory_, fde);
+
+ // Always print the cie information.
+ const DwarfCie* cie = fde->cie;
+ if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+ last_error_ = cfa.last_error();
+ return false;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
+ section_bias_ = section_bias;
+ entries_offset_ = offset;
+ next_entries_offset_ = offset;
+ entries_end_ = offset + size;
+
+ memory_.clear_func_offset();
+ memory_.clear_text_offset();
+ memory_.set_cur_offset(offset);
+ pc_offset_ = offset;
+
+ return true;
+}
+
+// Create a cached version of the fde information such that it is a std::map
+// that is indexed by end pc and contains a pair that represents the start pc
+// followed by the fde object. The fde pointers are owned by fde_entries_
+// and not by the map object.
+// It is possible for an fde to be represented by multiple entries in
+// the map. This can happen if the the start pc and end pc overlap already
+// existing entries. For example, if there is already an entry of 0x400, 0x200,
+// and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
+// will be added: 0x200, 0x100 and 0x500, 0x400.
+template <typename AddressType>
+void DwarfSectionImpl<AddressType>::InsertFde(const DwarfFde* fde) {
+ uint64_t start = fde->pc_start;
+ uint64_t end = fde->pc_end;
+ auto it = fdes_.upper_bound(start);
+ while (it != fdes_.end() && start < end && it->second.first < end) {
+ if (start < it->second.first) {
+ fdes_[it->second.first] = std::make_pair(start, fde);
+ }
+ start = it->first;
+ ++it;
+ }
+ if (start < end) {
+ fdes_[end] = std::make_pair(start, fde);
+ }
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::GetNextCieOrFde(const DwarfFde** fde_entry) {
+ uint64_t start_offset = next_entries_offset_;
+
+ memory_.set_data_offset(entries_offset_);
+ memory_.set_cur_offset(next_entries_offset_);
+ uint32_t value32;
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ uint64_t cie_offset;
+ uint8_t cie_fde_encoding;
+ bool entry_is_cie = false;
+ if (value32 == static_cast<uint32_t>(-1)) {
+ // 64 bit entry.
+ uint64_t value64;
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ next_entries_offset_ = memory_.cur_offset() + value64;
+ // Read the Cie Id of a Cie or the pointer of the Fde.
+ if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (value64 == cie64_value_) {
+ entry_is_cie = true;
+ cie_fde_encoding = DW_EH_PE_sdata8;
+ } else {
+ cie_offset = GetCieOffsetFromFde64(value64);
+ }
+ } else {
+ next_entries_offset_ = memory_.cur_offset() + value32;
+
+ // 32 bit Cie
+ if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+
+ if (value32 == cie32_value_) {
+ entry_is_cie = true;
+ cie_fde_encoding = DW_EH_PE_sdata4;
+ } else {
+ cie_offset = GetCieOffsetFromFde32(value32);
+ }
+ }
+
+ if (entry_is_cie) {
+ auto entry = cie_entries_.find(start_offset);
+ if (entry == cie_entries_.end()) {
+ DwarfCie* cie = &cie_entries_[start_offset];
+ cie->lsda_encoding = DW_EH_PE_omit;
+ cie->cfa_instructions_end = next_entries_offset_;
+ cie->fde_address_encoding = cie_fde_encoding;
+
+ if (!FillInCie(cie)) {
+ cie_entries_.erase(start_offset);
+ return false;
+ }
+ }
+ *fde_entry = nullptr;
+ } else {
+ auto entry = fde_entries_.find(start_offset);
+ if (entry != fde_entries_.end()) {
+ *fde_entry = &entry->second;
+ } else {
+ DwarfFde* fde = &fde_entries_[start_offset];
+ fde->cfa_instructions_end = next_entries_offset_;
+ fde->cie_offset = cie_offset;
+
+ if (!FillInFde(fde)) {
+ fde_entries_.erase(start_offset);
+ return false;
+ }
+ *fde_entry = fde;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+void DwarfSectionImpl<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+ // Loop through the already cached entries.
+ uint64_t entry_offset = entries_offset_;
+ while (entry_offset < next_entries_offset_) {
+ auto cie_it = cie_entries_.find(entry_offset);
+ if (cie_it != cie_entries_.end()) {
+ entry_offset = cie_it->second.cfa_instructions_end;
+ } else {
+ auto fde_it = fde_entries_.find(entry_offset);
+ if (fde_it == fde_entries_.end()) {
+ // No fde or cie at this entry, should not be possible.
+ return;
+ }
+ entry_offset = fde_it->second.cfa_instructions_end;
+ fdes->push_back(&fde_it->second);
+ }
+ }
+
+ while (next_entries_offset_ < entries_end_) {
+ const DwarfFde* fde;
+ if (!GetNextCieOrFde(&fde)) {
+ break;
+ }
+ if (fde != nullptr) {
+ InsertFde(fde);
+ fdes->push_back(fde);
+ }
+
+ if (next_entries_offset_ < memory_.cur_offset()) {
+ // Simply consider the processing done in this case.
+ break;
+ }
+ }
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromPc(uint64_t pc) {
+ // Search in the list of fdes we already have.
+ auto it = fdes_.upper_bound(pc);
+ if (it != fdes_.end()) {
+ if (pc >= it->second.first) {
+ return it->second.second;
+ }
+ }
+
+ // The section might have overlapping pcs in fdes, so it is necessary
+ // to do a linear search of the fdes by pc. As fdes are read, a cached
+ // search map is created.
+ while (next_entries_offset_ < entries_end_) {
+ const DwarfFde* fde;
+ if (!GetNextCieOrFde(&fde)) {
+ return nullptr;
+ }
+ if (fde != nullptr) {
+ InsertFde(fde);
+ if (pc >= fde->pc_start && pc < fde->pc_end) {
+ return fde;
+ }
+ }
+
+ if (next_entries_offset_ < memory_.cur_offset()) {
+ // Simply consider the processing done in this case.
+ break;
+ }
+ }
+ return nullptr;
+}
+
+// Explicitly instantiate DwarfSectionImpl
+template class DwarfSectionImpl<uint32_t>;
+template class DwarfSectionImpl<uint64_t>;
+
+// Explicitly instantiate DwarfDebugFrame
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+// Explicitly instantiate DwarfEhFrame
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
+} // namespace unwindstack
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
new file mode 100644
index 000000000..f01b0926b
--- /dev/null
+++ b/libunwindstack/Elf.cpp
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfInterfaceArm.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+bool Elf::cache_enabled_;
+std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
+std::mutex* Elf::cache_lock_;
+
+bool Elf::Init() {
+ load_bias_ = 0;
+ if (!memory_) {
+ return false;
+ }
+
+ interface_.reset(CreateInterfaceFromMemory(memory_.get()));
+ if (!interface_) {
+ return false;
+ }
+
+ valid_ = interface_->Init(&load_bias_);
+ if (valid_) {
+ interface_->InitHeaders();
+ InitGnuDebugdata();
+ } else {
+ interface_.reset(nullptr);
+ }
+ return valid_;
+}
+
+// It is expensive to initialize the .gnu_debugdata section. Provide a method
+// to initialize this data separately.
+void Elf::InitGnuDebugdata() {
+ if (!valid_ || interface_->gnu_debugdata_offset() == 0) {
+ return;
+ }
+
+ gnu_debugdata_memory_.reset(interface_->CreateGnuDebugdataMemory());
+ gnu_debugdata_interface_.reset(CreateInterfaceFromMemory(gnu_debugdata_memory_.get()));
+ ElfInterface* gnu = gnu_debugdata_interface_.get();
+ if (gnu == nullptr) {
+ return;
+ }
+
+ // Ignore the load_bias from the compressed section, the correct load bias
+ // is in the uncompressed data.
+ int64_t load_bias;
+ if (gnu->Init(&load_bias)) {
+ gnu->InitHeaders();
+ interface_->SetGnuDebugdataInterface(gnu);
+ } else {
+ // Free all of the memory associated with the gnu_debugdata section.
+ gnu_debugdata_memory_.reset(nullptr);
+ gnu_debugdata_interface_.reset(nullptr);
+ }
+}
+
+void Elf::Invalidate() {
+ interface_.reset(nullptr);
+ valid_ = false;
+}
+
+std::string Elf::GetSoname() {
+ std::lock_guard<std::mutex> guard(lock_);
+ if (!valid_) {
+ return "";
+ }
+ return interface_->GetSoname();
+}
+
+uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
+ return pc - map_info->start + load_bias_ + map_info->elf_offset;
+}
+
+bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+ std::lock_guard<std::mutex> guard(lock_);
+ return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
+}
+
+bool Elf::GetGlobalVariableOffset(const std::string& name, uint64_t* memory_offset) {
+ if (!valid_) {
+ return false;
+ }
+
+ uint64_t vaddr;
+ if (!interface_->GetGlobalVariable(name, &vaddr) &&
+ (gnu_debugdata_interface_ == nullptr ||
+ !gnu_debugdata_interface_->GetGlobalVariable(name, &vaddr))) {
+ return false;
+ }
+
+ // Check the .data section.
+ uint64_t vaddr_start = interface_->data_vaddr_start();
+ if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
+ *memory_offset = vaddr - vaddr_start + interface_->data_offset();
+ return true;
+ }
+
+ // Check the .dynamic section.
+ vaddr_start = interface_->dynamic_vaddr_start();
+ if (vaddr >= vaddr_start && vaddr < interface_->dynamic_vaddr_end()) {
+ *memory_offset = vaddr - vaddr_start + interface_->dynamic_offset();
+ return true;
+ }
+
+ return false;
+}
+
+std::string Elf::GetBuildID() {
+ if (!valid_) {
+ return "";
+ }
+ return interface_->GetBuildID();
+}
+
+void Elf::GetLastError(ErrorData* data) {
+ if (valid_) {
+ *data = interface_->last_error();
+ }
+}
+
+ErrorCode Elf::GetLastErrorCode() {
+ if (valid_) {
+ return interface_->LastErrorCode();
+ }
+ return ERROR_INVALID_ELF;
+}
+
+uint64_t Elf::GetLastErrorAddress() {
+ if (valid_) {
+ return interface_->LastErrorAddress();
+ }
+ return 0;
+}
+
+// The relative pc expectd by this function is relative to the start of the elf.
+bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+ if (!valid_) {
+ return false;
+ }
+
+ // Convert the rel_pc to an elf_offset.
+ if (rel_pc < static_cast<uint64_t>(load_bias_)) {
+ return false;
+ }
+ return regs->StepIfSignalHandler(rel_pc - load_bias_, this, process_memory);
+}
+
+// The relative pc is always relative to the start of the map from which it comes.
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
+ if (!valid_) {
+ return false;
+ }
+
+ // Lock during the step which can update information in the object.
+ std::lock_guard<std::mutex> guard(lock_);
+ return interface_->Step(rel_pc, regs, process_memory, finished);
+}
+
+bool Elf::IsValidElf(Memory* memory) {
+ if (memory == nullptr) {
+ return false;
+ }
+
+ // Verify that this is a valid elf file.
+ uint8_t e_ident[SELFMAG + 1];
+ if (!memory->ReadFully(0, e_ident, SELFMAG)) {
+ return false;
+ }
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
+ return false;
+ }
+ return true;
+}
+
+bool Elf::GetInfo(Memory* memory, uint64_t* size) {
+ if (!IsValidElf(memory)) {
+ return false;
+ }
+ *size = 0;
+
+ uint8_t class_type;
+ if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
+ return false;
+ }
+
+ // Get the maximum size of the elf data from the header.
+ if (class_type == ELFCLASS32) {
+ ElfInterface32::GetMaxSize(memory, size);
+ } else if (class_type == ELFCLASS64) {
+ ElfInterface64::GetMaxSize(memory, size);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool Elf::IsValidPc(uint64_t pc) {
+ if (!valid_ || (load_bias_ > 0 && pc < static_cast<uint64_t>(load_bias_))) {
+ return false;
+ }
+
+ if (interface_->IsValidPc(pc)) {
+ return true;
+ }
+
+ if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) {
+ return true;
+ }
+
+ return false;
+}
+
+ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
+ if (!IsValidElf(memory)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<ElfInterface> interface;
+ if (!memory->ReadFully(EI_CLASS, &class_type_, 1)) {
+ return nullptr;
+ }
+ if (class_type_ == ELFCLASS32) {
+ Elf32_Half e_machine;
+ if (!memory->ReadFully(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) {
+ return nullptr;
+ }
+
+ machine_type_ = e_machine;
+ if (e_machine == EM_ARM) {
+ arch_ = ARCH_ARM;
+ interface.reset(new ElfInterfaceArm(memory));
+ } else if (e_machine == EM_386) {
+ arch_ = ARCH_X86;
+ interface.reset(new ElfInterface32(memory));
+ } else if (e_machine == EM_MIPS) {
+ arch_ = ARCH_MIPS;
+ interface.reset(new ElfInterface32(memory));
+ } else {
+ // Unsupported.
+ ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
+ return nullptr;
+ }
+ } else if (class_type_ == ELFCLASS64) {
+ Elf64_Half e_machine;
+ if (!memory->ReadFully(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
+ return nullptr;
+ }
+
+ machine_type_ = e_machine;
+ if (e_machine == EM_AARCH64) {
+ arch_ = ARCH_ARM64;
+ } else if (e_machine == EM_X86_64) {
+ arch_ = ARCH_X86_64;
+ } else if (e_machine == EM_MIPS) {
+ arch_ = ARCH_MIPS64;
+ } else {
+ // Unsupported.
+ ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
+ e_machine);
+ return nullptr;
+ }
+ interface.reset(new ElfInterface64(memory));
+ }
+
+ return interface.release();
+}
+
+int64_t Elf::GetLoadBias(Memory* memory) {
+ if (!IsValidElf(memory)) {
+ return 0;
+ }
+
+ uint8_t class_type;
+ if (!memory->Read(EI_CLASS, &class_type, 1)) {
+ return 0;
+ }
+
+ if (class_type == ELFCLASS32) {
+ return ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(memory);
+ } else if (class_type == ELFCLASS64) {
+ return ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(memory);
+ }
+ return 0;
+}
+
+void Elf::SetCachingEnabled(bool enable) {
+ if (!cache_enabled_ && enable) {
+ cache_enabled_ = true;
+ cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
+ cache_lock_ = new std::mutex;
+ } else if (cache_enabled_ && !enable) {
+ cache_enabled_ = false;
+ delete cache_;
+ delete cache_lock_;
+ }
+}
+
+void Elf::CacheLock() {
+ cache_lock_->lock();
+}
+
+void Elf::CacheUnlock() {
+ cache_lock_->unlock();
+}
+
+void Elf::CacheAdd(MapInfo* info) {
+ // If elf_offset != 0, then cache both name:offset and name.
+ // The cached name is used to do lookups if multiple maps for the same
+ // named elf file exist.
+ // For example, if there are two maps boot.odex:1000 and boot.odex:2000
+ // where each reference the entire boot.odex, the cache will properly
+ // use the same cached elf object.
+
+ if (info->offset == 0 || info->elf_offset != 0) {
+ (*cache_)[info->name] = std::make_pair(info->elf, true);
+ }
+
+ if (info->offset != 0) {
+ // The second element in the pair indicates whether elf_offset should
+ // be set to offset when getting out of the cache.
+ (*cache_)[info->name + ':' + std::to_string(info->offset)] =
+ std::make_pair(info->elf, info->elf_offset != 0);
+ }
+}
+
+bool Elf::CacheAfterCreateMemory(MapInfo* info) {
+ if (info->name.empty() || info->offset == 0 || info->elf_offset == 0) {
+ return false;
+ }
+
+ auto entry = cache_->find(info->name);
+ if (entry == cache_->end()) {
+ return false;
+ }
+
+ // In this case, the whole file is the elf, and the name has already
+ // been cached. Add an entry at name:offset to get this directly out
+ // of the cache next time.
+ info->elf = entry->second.first;
+ (*cache_)[info->name + ':' + std::to_string(info->offset)] = std::make_pair(info->elf, true);
+ return true;
+}
+
+bool Elf::CacheGet(MapInfo* info) {
+ std::string name(info->name);
+ if (info->offset != 0) {
+ name += ':' + std::to_string(info->offset);
+ }
+ auto entry = cache_->find(name);
+ if (entry != cache_->end()) {
+ info->elf = entry->second.first;
+ if (entry->second.second) {
+ info->elf_offset = info->offset;
+ }
+ return true;
+ }
+ return false;
+}
+
+std::string Elf::GetBuildID(Memory* memory) {
+ if (!IsValidElf(memory)) {
+ return "";
+ }
+
+ uint8_t class_type;
+ if (!memory->Read(EI_CLASS, &class_type, 1)) {
+ return "";
+ }
+
+ if (class_type == ELFCLASS32) {
+ return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
+ } else if (class_type == ELFCLASS64) {
+ return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
+ }
+ return "";
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
new file mode 100644
index 000000000..341275dbb
--- /dev/null
+++ b/libunwindstack/ElfInterface.cpp
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "DwarfEhFrameWithHdr.h"
+#include "MemoryBuffer.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+ElfInterface::~ElfInterface() {
+ for (auto symbol : symbols_) {
+ delete symbol;
+ }
+}
+
+bool ElfInterface::IsValidPc(uint64_t pc) {
+ if (!pt_loads_.empty()) {
+ for (auto& entry : pt_loads_) {
+ uint64_t start = entry.second.table_offset;
+ uint64_t end = start + entry.second.table_size;
+ if (pc >= start && pc < end) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // No PT_LOAD data, look for a fde for this pc in the section data.
+ if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
+ return true;
+ }
+
+ if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
+ return true;
+ }
+
+ return false;
+}
+
+Memory* ElfInterface::CreateGnuDebugdataMemory() {
+ if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
+ return nullptr;
+ }
+
+ // TODO: Only call these initialization functions once.
+ CrcGenerateTable();
+ Crc64GenerateTable();
+
+ // Verify the request is not larger than the max size_t value.
+ if (gnu_debugdata_size_ > SIZE_MAX) {
+ return nullptr;
+ }
+ size_t initial_buffer_size;
+ if (__builtin_mul_overflow(5, gnu_debugdata_size_, &initial_buffer_size)) {
+ return nullptr;
+ }
+
+ size_t buffer_increment;
+ if (__builtin_mul_overflow(2, gnu_debugdata_size_, &buffer_increment)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<uint8_t[]> src(new (std::nothrow) uint8_t[gnu_debugdata_size_]);
+ if (src.get() == nullptr) {
+ return nullptr;
+ }
+
+ std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
+ if (!dst->Resize(initial_buffer_size)) {
+ return nullptr;
+ }
+
+ if (!memory_->ReadFully(gnu_debugdata_offset_, src.get(), gnu_debugdata_size_)) {
+ return nullptr;
+ }
+
+ ISzAlloc alloc;
+ CXzUnpacker state;
+ alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+ alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+ XzUnpacker_Construct(&state, &alloc);
+
+ int return_val;
+ size_t src_offset = 0;
+ size_t dst_offset = 0;
+ ECoderStatus status;
+ do {
+ size_t src_remaining = gnu_debugdata_size_ - src_offset;
+ size_t dst_remaining = dst->Size() - dst_offset;
+ if (dst_remaining < buffer_increment) {
+ size_t new_size;
+ if (__builtin_add_overflow(dst->Size(), buffer_increment, &new_size) ||
+ !dst->Resize(new_size)) {
+ XzUnpacker_Free(&state);
+ return nullptr;
+ }
+ dst_remaining += buffer_increment;
+ }
+ return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
+ &src_remaining, true, CODER_FINISH_ANY, &status);
+ src_offset += src_remaining;
+ dst_offset += dst_remaining;
+ } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
+ XzUnpacker_Free(&state);
+ if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
+ return nullptr;
+ }
+
+ // Shrink back down to the exact size.
+ if (!dst->Resize(dst_offset)) {
+ return nullptr;
+ }
+
+ return dst.release();
+}
+
+template <typename AddressType>
+void ElfInterface::InitHeadersWithTemplate() {
+ if (eh_frame_hdr_offset_ != 0) {
+ DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
+ eh_frame_.reset(eh_frame_hdr);
+ if (!eh_frame_hdr->EhFrameInit(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_) ||
+ !eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
+ eh_frame_.reset(nullptr);
+ }
+ }
+
+ if (eh_frame_.get() == nullptr && eh_frame_offset_ != 0) {
+ // If there is an eh_frame section without an eh_frame_hdr section,
+ // or using the frame hdr object failed to init.
+ eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+ if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_)) {
+ eh_frame_.reset(nullptr);
+ }
+ }
+
+ if (eh_frame_.get() == nullptr) {
+ eh_frame_hdr_offset_ = 0;
+ eh_frame_hdr_section_bias_ = 0;
+ eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
+ eh_frame_offset_ = 0;
+ eh_frame_section_bias_ = 0;
+ eh_frame_size_ = static_cast<uint64_t>(-1);
+ }
+
+ if (debug_frame_offset_ != 0) {
+ debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
+ if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, debug_frame_section_bias_)) {
+ debug_frame_.reset(nullptr);
+ debug_frame_offset_ = 0;
+ debug_frame_size_ = static_cast<uint64_t>(-1);
+ }
+ }
+}
+
+template <typename EhdrType, typename PhdrType, typename ShdrType>
+bool ElfInterface::ReadAllHeaders(int64_t* load_bias) {
+ EhdrType ehdr;
+ if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
+ last_error_.code = ERROR_MEMORY_INVALID;
+ last_error_.address = 0;
+ return false;
+ }
+
+ // If we have enough information that this is an elf file, then allow
+ // malformed program and section headers.
+ ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias);
+ ReadSectionHeaders<EhdrType, ShdrType>(ehdr);
+ return true;
+}
+
+template <typename EhdrType, typename PhdrType>
+int64_t ElfInterface::GetLoadBias(Memory* memory) {
+ EhdrType ehdr;
+ if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+ return false;
+ }
+
+ uint64_t offset = ehdr.e_phoff;
+ for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
+ PhdrType phdr;
+ if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
+ return 0;
+ }
+
+ // Find the first executable load when looking for the load bias.
+ if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
+ return static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
+ }
+ }
+ return 0;
+}
+
+template <typename EhdrType, typename PhdrType>
+void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias) {
+ uint64_t offset = ehdr.e_phoff;
+ bool first_exec_load_header = true;
+ for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
+ PhdrType phdr;
+ if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
+ return;
+ }
+
+ switch (phdr.p_type) {
+ case PT_LOAD:
+ {
+ if ((phdr.p_flags & PF_X) == 0) {
+ continue;
+ }
+
+ pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
+ static_cast<size_t>(phdr.p_memsz)};
+ // Only set the load bias from the first executable load header.
+ if (first_exec_load_header) {
+ *load_bias = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
+ }
+ first_exec_load_header = false;
+ break;
+ }
+
+ case PT_GNU_EH_FRAME:
+ // This is really the pointer to the .eh_frame_hdr section.
+ eh_frame_hdr_offset_ = phdr.p_offset;
+ eh_frame_hdr_section_bias_ = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
+ eh_frame_hdr_size_ = phdr.p_memsz;
+ break;
+
+ case PT_DYNAMIC:
+ dynamic_offset_ = phdr.p_offset;
+ dynamic_vaddr_start_ = phdr.p_vaddr;
+ if (__builtin_add_overflow(dynamic_vaddr_start_, phdr.p_memsz, &dynamic_vaddr_end_)) {
+ dynamic_offset_ = 0;
+ dynamic_vaddr_start_ = 0;
+ dynamic_vaddr_end_ = 0;
+ }
+ break;
+
+ default:
+ HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
+ break;
+ }
+ }
+}
+
+template <typename NhdrType>
+std::string ElfInterface::ReadBuildID() {
+ // Ensure there is no overflow in any of the calulations below.
+ uint64_t tmp;
+ if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
+ return "";
+ }
+
+ uint64_t offset = 0;
+ while (offset < gnu_build_id_size_) {
+ if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
+ return "";
+ }
+ NhdrType hdr;
+ if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
+ return "";
+ }
+ offset += sizeof(hdr);
+
+ if (gnu_build_id_size_ - offset < hdr.n_namesz) {
+ return "";
+ }
+ if (hdr.n_namesz > 0) {
+ std::string name(hdr.n_namesz, '\0');
+ if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
+ return "";
+ }
+
+ // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+ if (name.back() == '\0')
+ name.resize(name.size() - 1);
+
+ // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_namesz + 3) & ~3;
+
+ if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+ if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+ return "";
+ }
+ std::string build_id(hdr.n_descsz, '\0');
+ if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
+ return build_id;
+ }
+ return "";
+ }
+ }
+ // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_descsz + 3) & ~3;
+ }
+ return "";
+}
+
+template <typename EhdrType, typename ShdrType>
+void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
+ uint64_t offset = ehdr.e_shoff;
+ uint64_t sec_offset = 0;
+ uint64_t sec_size = 0;
+
+ // Get the location of the section header names.
+ // If something is malformed in the header table data, we aren't going
+ // to terminate, we'll simply ignore this part.
+ ShdrType shdr;
+ if (ehdr.e_shstrndx < ehdr.e_shnum) {
+ uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+ if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+ sec_offset = shdr.sh_offset;
+ sec_size = shdr.sh_size;
+ }
+ }
+
+ // Skip the first header, it's always going to be NULL.
+ offset += ehdr.e_shentsize;
+ for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
+ if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
+ return;
+ }
+
+ if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
+ // Need to go get the information about the section that contains
+ // the string terminated names.
+ ShdrType str_shdr;
+ if (shdr.sh_link >= ehdr.e_shnum) {
+ continue;
+ }
+ uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
+ if (!memory_->ReadFully(str_offset, &str_shdr, sizeof(str_shdr))) {
+ continue;
+ }
+ if (str_shdr.sh_type != SHT_STRTAB) {
+ continue;
+ }
+ symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
+ str_shdr.sh_offset, str_shdr.sh_size));
+ } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
+ // Look for the .debug_frame and .gnu_debugdata.
+ if (shdr.sh_name < sec_size) {
+ std::string name;
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ if (name == ".debug_frame") {
+ debug_frame_offset_ = shdr.sh_offset;
+ debug_frame_size_ = shdr.sh_size;
+ debug_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
+ } else if (name == ".gnu_debugdata") {
+ gnu_debugdata_offset_ = shdr.sh_offset;
+ gnu_debugdata_size_ = shdr.sh_size;
+ } else if (name == ".eh_frame") {
+ eh_frame_offset_ = shdr.sh_offset;
+ eh_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
+ eh_frame_size_ = shdr.sh_size;
+ } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
+ eh_frame_hdr_offset_ = shdr.sh_offset;
+ eh_frame_hdr_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
+ eh_frame_hdr_size_ = shdr.sh_size;
+ } else if (name == ".data") {
+ data_offset_ = shdr.sh_offset;
+ data_vaddr_start_ = shdr.sh_addr;
+ if (__builtin_add_overflow(data_vaddr_start_, shdr.sh_size, &data_vaddr_end_)) {
+ data_offset_ = 0;
+ data_vaddr_start_ = 0;
+ data_vaddr_end_ = 0;
+ }
+ }
+ }
+ }
+ } else if (shdr.sh_type == SHT_STRTAB) {
+ // In order to read soname, keep track of address to offset mapping.
+ strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
+ static_cast<uint64_t>(shdr.sh_offset)));
+ } else if (shdr.sh_type == SHT_NOTE) {
+ if (shdr.sh_name < sec_size) {
+ std::string name;
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+ name == ".note.gnu.build-id") {
+ gnu_build_id_offset_ = shdr.sh_offset;
+ gnu_build_id_size_ = shdr.sh_size;
+ }
+ }
+ }
+ }
+}
+
+template <typename DynType>
+std::string ElfInterface::GetSonameWithTemplate() {
+ if (soname_type_ == SONAME_INVALID) {
+ return "";
+ }
+ if (soname_type_ == SONAME_VALID) {
+ return soname_;
+ }
+
+ soname_type_ = SONAME_INVALID;
+
+ uint64_t soname_offset = 0;
+ uint64_t strtab_addr = 0;
+ uint64_t strtab_size = 0;
+
+ // Find the soname location from the dynamic headers section.
+ DynType dyn;
+ uint64_t offset = dynamic_offset_;
+ uint64_t max_offset = offset + dynamic_vaddr_end_ - dynamic_vaddr_start_;
+ for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
+ if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
+ last_error_.code = ERROR_MEMORY_INVALID;
+ last_error_.address = offset;
+ return "";
+ }
+
+ if (dyn.d_tag == DT_STRTAB) {
+ strtab_addr = dyn.d_un.d_ptr;
+ } else if (dyn.d_tag == DT_STRSZ) {
+ strtab_size = dyn.d_un.d_val;
+ } else if (dyn.d_tag == DT_SONAME) {
+ soname_offset = dyn.d_un.d_val;
+ } else if (dyn.d_tag == DT_NULL) {
+ break;
+ }
+ }
+
+ // Need to map the strtab address to the real offset.
+ for (const auto& entry : strtabs_) {
+ if (entry.first == strtab_addr) {
+ soname_offset = entry.second + soname_offset;
+ if (soname_offset >= entry.second + strtab_size) {
+ return "";
+ }
+ if (!memory_->ReadString(soname_offset, &soname_)) {
+ return "";
+ }
+ soname_type_ = SONAME_VALID;
+ return soname_;
+ }
+ }
+ return "";
+}
+
+template <typename SymType>
+bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
+ uint64_t* func_offset) {
+ if (symbols_.empty()) {
+ return false;
+ }
+
+ for (const auto symbol : symbols_) {
+ if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename SymType>
+bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
+ if (symbols_.empty()) {
+ return false;
+ }
+
+ for (const auto symbol : symbols_) {
+ if (symbol->GetGlobal<SymType>(memory_, name, memory_address)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+ last_error_.code = ERROR_NONE;
+ last_error_.address = 0;
+
+ // Try the debug_frame first since it contains the most specific unwind
+ // information.
+ DwarfSection* debug_frame = debug_frame_.get();
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
+ return true;
+ }
+
+ // Try the eh_frame next.
+ DwarfSection* eh_frame = eh_frame_.get();
+ if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
+ return true;
+ }
+
+ if (gnu_debugdata_interface_ != nullptr &&
+ gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
+ return true;
+ }
+
+ // Set the error code based on the first error encountered.
+ DwarfSection* section = nullptr;
+ if (debug_frame_ != nullptr) {
+ section = debug_frame_.get();
+ } else if (eh_frame_ != nullptr) {
+ section = eh_frame_.get();
+ } else if (gnu_debugdata_interface_ != nullptr) {
+ last_error_ = gnu_debugdata_interface_->last_error();
+ return false;
+ } else {
+ return false;
+ }
+
+ // Convert the DWARF ERROR to an external error.
+ DwarfErrorCode code = section->LastErrorCode();
+ switch (code) {
+ case DWARF_ERROR_NONE:
+ last_error_.code = ERROR_NONE;
+ break;
+
+ case DWARF_ERROR_MEMORY_INVALID:
+ last_error_.code = ERROR_MEMORY_INVALID;
+ last_error_.address = section->LastErrorAddress();
+ break;
+
+ case DWARF_ERROR_ILLEGAL_VALUE:
+ case DWARF_ERROR_ILLEGAL_STATE:
+ case DWARF_ERROR_STACK_INDEX_NOT_VALID:
+ case DWARF_ERROR_TOO_MANY_ITERATIONS:
+ case DWARF_ERROR_CFA_NOT_DEFINED:
+ case DWARF_ERROR_NO_FDES:
+ last_error_.code = ERROR_UNWIND_INFO;
+ break;
+
+ case DWARF_ERROR_NOT_IMPLEMENTED:
+ case DWARF_ERROR_UNSUPPORTED_VERSION:
+ last_error_.code = ERROR_UNSUPPORTED;
+ break;
+ }
+ return false;
+}
+
+// This is an estimation of the size of the elf file using the location
+// of the section headers and size. This assumes that the section headers
+// are at the end of the elf file. If the elf has a load bias, the size
+// will be too large, but this is acceptable.
+template <typename EhdrType>
+void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
+ EhdrType ehdr;
+ if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+ return;
+ }
+ if (ehdr.e_shnum == 0) {
+ return;
+ }
+ *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
+}
+
+template <typename EhdrType, typename ShdrType>
+bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
+ EhdrType ehdr;
+ if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+ return false;
+ }
+
+ uint64_t offset = ehdr.e_shoff;
+ uint64_t sec_offset;
+ uint64_t sec_size;
+ ShdrType shdr;
+ if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+ return false;
+ }
+
+ uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+ if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ sec_offset = shdr.sh_offset;
+ sec_size = shdr.sh_size;
+
+ // Skip the first header, it's always going to be NULL.
+ offset += ehdr.e_shentsize;
+ for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
+ if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ std::string name;
+ if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
+ memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+ *build_id_offset = shdr.sh_offset;
+ *build_id_size = shdr.sh_size;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <typename EhdrType, typename ShdrType, typename NhdrType>
+std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
+ uint64_t note_offset;
+ uint64_t note_size;
+ if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, &note_offset, &note_size)) {
+ return "";
+ }
+
+ // Ensure there is no overflow in any of the calculations below.
+ uint64_t tmp;
+ if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
+ return "";
+ }
+
+ uint64_t offset = 0;
+ while (offset < note_size) {
+ if (note_size - offset < sizeof(NhdrType)) {
+ return "";
+ }
+ NhdrType hdr;
+ if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
+ return "";
+ }
+ offset += sizeof(hdr);
+
+ if (note_size - offset < hdr.n_namesz) {
+ return "";
+ }
+ if (hdr.n_namesz > 0) {
+ std::string name(hdr.n_namesz, '\0');
+ if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
+ return "";
+ }
+
+ // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+ if (name.back() == '\0') name.resize(name.size() - 1);
+
+ // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_namesz + 3) & ~3;
+
+ if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+ if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+ return "";
+ }
+ std::string build_id(hdr.n_descsz - 1, '\0');
+ if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
+ return build_id;
+ }
+ return "";
+ }
+ }
+ // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_descsz + 3) & ~3;
+ }
+ return "";
+}
+
+// Instantiate all of the needed template functions.
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+
+template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(int64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(int64_t*);
+
+template void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, int64_t*);
+template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, int64_t*);
+
+template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
+template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
+
+template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
+template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
+
+template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
+template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
+
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
+ uint64_t*);
+template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
+ uint64_t*);
+
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
+
+template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
+
+template int64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
+template int64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
+ Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
+ Memory*);
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
new file mode 100644
index 000000000..76f2dc882
--- /dev/null
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+bool ElfInterfaceArm::Init(int64_t* load_bias) {
+ if (!ElfInterface32::Init(load_bias)) {
+ return false;
+ }
+ load_bias_ = *load_bias;
+ return true;
+}
+
+bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
+ if (start_offset_ == 0 || total_entries_ == 0) {
+ last_error_.code = ERROR_UNWIND_INFO;
+ return false;
+ }
+
+ size_t first = 0;
+ size_t last = total_entries_;
+ while (first < last) {
+ size_t current = (first + last) / 2;
+ uint32_t addr = addrs_[current];
+ if (addr == 0) {
+ if (!GetPrel31Addr(start_offset_ + current * 8, &addr)) {
+ return false;
+ }
+ addrs_[current] = addr;
+ }
+ if (pc == addr) {
+ *entry_offset = start_offset_ + current * 8;
+ return true;
+ }
+ if (pc < addr) {
+ last = current;
+ } else {
+ first = current + 1;
+ }
+ }
+ if (last != 0) {
+ *entry_offset = start_offset_ + (last - 1) * 8;
+ return true;
+ }
+ last_error_.code = ERROR_UNWIND_INFO;
+ return false;
+}
+
+bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) {
+ uint32_t data;
+ if (!memory_->Read32(offset, &data)) {
+ last_error_.code = ERROR_MEMORY_INVALID;
+ last_error_.address = offset;
+ return false;
+ }
+
+ // Sign extend the value if necessary.
+ int32_t value = (static_cast<int32_t>(data) << 1) >> 1;
+ *addr = offset + value;
+ return true;
+}
+
+#if !defined(PT_ARM_EXIDX)
+#define PT_ARM_EXIDX 0x70000001
+#endif
+
+void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) {
+ if (type != PT_ARM_EXIDX) {
+ return;
+ }
+
+ // The offset already takes into account the load bias.
+ start_offset_ = ph_offset;
+
+ // Always use filesz instead of memsz. In most cases they are the same,
+ // but some shared libraries wind up setting one correctly and not the other.
+ total_entries_ = ph_filesz / 8;
+}
+
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+ // Dwarf unwind information is precise about whether a pc is covered or not,
+ // but arm unwind information only has ranges of pc. In order to avoid
+ // incorrectly doing a bad unwind using arm unwind information for a
+ // different function, always try and unwind with the dwarf information first.
+ return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+ StepExidx(pc, regs, process_memory, finished);
+}
+
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+ // Adjust the load bias to get the real relative pc.
+ if (pc < load_bias_) {
+ last_error_.code = ERROR_UNWIND_INFO;
+ return false;
+ }
+ pc -= load_bias_;
+
+ RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
+ uint64_t entry_offset;
+ if (!FindEntry(pc, &entry_offset)) {
+ return false;
+ }
+
+ ArmExidx arm(regs_arm, memory_, process_memory);
+ arm.set_cfa(regs_arm->sp());
+ bool return_value = false;
+ if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
+ // If the pc was not set, then use the LR registers for the PC.
+ if (!arm.pc_set()) {
+ (*regs_arm)[ARM_REG_PC] = (*regs_arm)[ARM_REG_LR];
+ }
+ (*regs_arm)[ARM_REG_SP] = arm.cfa();
+ return_value = true;
+
+ // If the pc was set to zero, consider this the final frame.
+ *finished = (regs_arm->pc() == 0) ? true : false;
+ }
+
+ if (arm.status() == ARM_STATUS_NO_UNWIND) {
+ *finished = true;
+ return true;
+ }
+
+ if (!return_value) {
+ switch (arm.status()) {
+ case ARM_STATUS_NONE:
+ case ARM_STATUS_NO_UNWIND:
+ case ARM_STATUS_FINISH:
+ last_error_.code = ERROR_NONE;
+ break;
+
+ case ARM_STATUS_RESERVED:
+ case ARM_STATUS_SPARE:
+ case ARM_STATUS_TRUNCATED:
+ case ARM_STATUS_MALFORMED:
+ case ARM_STATUS_INVALID_ALIGNMENT:
+ case ARM_STATUS_INVALID_PERSONALITY:
+ last_error_.code = ERROR_UNWIND_INFO;
+ break;
+
+ case ARM_STATUS_READ_FAILED:
+ last_error_.code = ERROR_MEMORY_INVALID;
+ last_error_.address = arm.status_address();
+ break;
+ }
+ }
+ return return_value;
+}
+
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) {
+ // For ARM, thumb function symbols have bit 0 set, but the address passed
+ // in here might not have this bit set and result in a failure to find
+ // the thumb function names. Adjust the address and offset to account
+ // for this possible case.
+ if (ElfInterface32::GetFunctionName(addr | 1, name, offset)) {
+ *offset &= ~1;
+ return true;
+ }
+ return false;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
new file mode 100644
index 000000000..1d71cacd1
--- /dev/null
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
+#define _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <iterator>
+#include <unordered_map>
+
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class ElfInterfaceArm : public ElfInterface32 {
+ public:
+ ElfInterfaceArm(Memory* memory) : ElfInterface32(memory) {}
+ virtual ~ElfInterfaceArm() = default;
+
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, uint32_t> {
+ public:
+ iterator(ElfInterfaceArm* interface, size_t index) : interface_(interface), index_(index) { }
+
+ iterator& operator++() { index_++; return *this; }
+ iterator& operator++(int increment) { index_ += increment; return *this; }
+ iterator& operator--() { index_--; return *this; }
+ iterator& operator--(int decrement) { index_ -= decrement; return *this; }
+
+ bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+ bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+ uint32_t operator*() {
+ uint32_t addr = interface_->addrs_[index_];
+ if (addr == 0) {
+ if (!interface_->GetPrel31Addr(interface_->start_offset_ + index_ * 8, &addr)) {
+ return 0;
+ }
+ interface_->addrs_[index_] = addr;
+ }
+ return addr;
+ }
+
+ private:
+ ElfInterfaceArm* interface_ = nullptr;
+ size_t index_ = 0;
+ };
+
+ iterator begin() { return iterator(this, 0); }
+ iterator end() { return iterator(this, total_entries_); }
+
+ bool Init(int64_t* section_bias) override;
+
+ bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
+
+ bool FindEntry(uint32_t pc, uint64_t* entry_offset);
+
+ void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
+
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
+
+ bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
+
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
+
+ uint64_t start_offset() { return start_offset_; }
+
+ size_t total_entries() { return total_entries_; }
+
+ void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
+
+ protected:
+ uint64_t start_offset_ = 0;
+ size_t total_entries_ = 0;
+ uint64_t load_bias_ = 0;
+
+ std::unordered_map<size_t, uint32_t> addrs_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
new file mode 100644
index 000000000..ee6c8a593
--- /dev/null
+++ b/libunwindstack/Global.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+Global::Global(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+ : memory_(memory), search_libs_(search_libs) {}
+
+void Global::SetArch(ArchEnum arch) {
+ if (arch_ == ARCH_UNKNOWN) {
+ arch_ = arch;
+ ProcessArch();
+ }
+}
+
+bool Global::Searchable(const std::string& name) {
+ if (search_libs_.empty()) {
+ return true;
+ }
+
+ if (name.empty()) {
+ return false;
+ }
+
+ const char* base_name = basename(name.c_str());
+ for (const std::string& lib : search_libs_) {
+ if (base_name == lib) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
+ std::string variable(var_str);
+ // When looking for global variables, do not arbitrarily search every
+ // readable map. Instead look for a specific pattern that must exist.
+ // The pattern should be a readable map, followed by a read-write
+ // map with a non-zero offset.
+ // For example:
+ // f0000-f1000 0 r-- /system/lib/libc.so
+ // f1000-f2000 1000 r-x /system/lib/libc.so
+ // f2000-f3000 2000 rw- /system/lib/libc.so
+ // This also works:
+ // f0000-f2000 0 r-- /system/lib/libc.so
+ // f2000-f3000 2000 rw- /system/lib/libc.so
+ // It is also possible to see empty maps after the read-only like so:
+ // f0000-f1000 0 r-- /system/lib/libc.so
+ // f1000-f2000 0 ---
+ // f2000-f3000 1000 r-x /system/lib/libc.so
+ // f3000-f4000 2000 rw- /system/lib/libc.so
+ MapInfo* map_zero = nullptr;
+ for (const auto& info : *maps) {
+ if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+ map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
+ Elf* elf = map_zero->GetElf(memory_, arch());
+ uint64_t ptr;
+ if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
+ uint64_t offset_end = info->offset + info->end - info->start;
+ if (ptr >= info->offset && ptr < offset_end) {
+ ptr = info->start + ptr - info->offset;
+ if (ReadVariableData(ptr)) {
+ break;
+ }
+ }
+ }
+ } else if (info->offset == 0 && !info->name.empty()) {
+ map_zero = info.get();
+ }
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
new file mode 100644
index 000000000..8a85607cb
--- /dev/null
+++ b/libunwindstack/JitDebug.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+
+#include "MemoryRange.h"
+
+// This implements the JIT Compilation Interface.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+
+namespace unwindstack {
+
+struct JITCodeEntry32Pack {
+ uint32_t next;
+ uint32_t prev;
+ uint32_t symfile_addr;
+ uint64_t symfile_size;
+} __attribute__((packed));
+
+struct JITCodeEntry32Pad {
+ uint32_t next;
+ uint32_t prev;
+ uint32_t symfile_addr;
+ uint32_t pad;
+ uint64_t symfile_size;
+};
+
+struct JITCodeEntry64 {
+ uint64_t next;
+ uint64_t prev;
+ uint64_t symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct JITDescriptorHeader {
+ uint32_t version;
+ uint32_t action_flag;
+};
+
+struct JITDescriptor32 {
+ JITDescriptorHeader header;
+ uint32_t relevant_entry;
+ uint32_t first_entry;
+};
+
+struct JITDescriptor64 {
+ JITDescriptorHeader header;
+ uint64_t relevant_entry;
+ uint64_t first_entry;
+};
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+ : Global(memory, search_libs) {}
+
+JitDebug::~JitDebug() {
+ for (auto* elf : elf_list_) {
+ delete elf;
+ }
+}
+
+uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
+ JITDescriptor32 desc;
+ if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+ return 0;
+ }
+
+ if (desc.header.version != 1 || desc.first_entry == 0) {
+ // Either unknown version, or no jit entries.
+ return 0;
+ }
+
+ return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
+ JITDescriptor64 desc;
+ if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+ return 0;
+ }
+
+ if (desc.header.version != 1 || desc.first_entry == 0) {
+ // Either unknown version, or no jit entries.
+ return 0;
+ }
+
+ return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
+ JITCodeEntry32Pack code;
+ if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+ return 0;
+ }
+
+ *start = code.symfile_addr;
+ *size = code.symfile_size;
+ return code.next;
+}
+
+uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
+ JITCodeEntry32Pad code;
+ if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+ return 0;
+ }
+
+ *start = code.symfile_addr;
+ *size = code.symfile_size;
+ return code.next;
+}
+
+uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
+ JITCodeEntry64 code;
+ if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+ return 0;
+ }
+
+ *start = code.symfile_addr;
+ *size = code.symfile_size;
+ return code.next;
+}
+
+void JitDebug::ProcessArch() {
+ switch (arch()) {
+ case ARCH_X86:
+ read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+ read_entry_func_ = &JitDebug::ReadEntry32Pack;
+ break;
+
+ case ARCH_ARM:
+ case ARCH_MIPS:
+ read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+ read_entry_func_ = &JitDebug::ReadEntry32Pad;
+ break;
+
+ case ARCH_ARM64:
+ case ARCH_X86_64:
+ case ARCH_MIPS64:
+ read_descriptor_func_ = &JitDebug::ReadDescriptor64;
+ read_entry_func_ = &JitDebug::ReadEntry64;
+ break;
+ case ARCH_UNKNOWN:
+ abort();
+ }
+}
+
+bool JitDebug::ReadVariableData(uint64_t ptr) {
+ entry_addr_ = (this->*read_descriptor_func_)(ptr);
+ return entry_addr_ != 0;
+}
+
+void JitDebug::Init(Maps* maps) {
+ if (initialized_) {
+ return;
+ }
+ // Regardless of what happens below, consider the init finished.
+ initialized_ = true;
+
+ FindAndReadVariable(maps, "__jit_debug_descriptor");
+}
+
+Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
+ // Use a single lock, this object should be used so infrequently that
+ // a fine grain lock is unnecessary.
+ std::lock_guard<std::mutex> guard(lock_);
+ if (!initialized_) {
+ Init(maps);
+ }
+
+ // Search the existing elf object first.
+ for (Elf* elf : elf_list_) {
+ if (elf->IsValidPc(pc)) {
+ return elf;
+ }
+ }
+
+ while (entry_addr_ != 0) {
+ uint64_t start;
+ uint64_t size;
+ entry_addr_ = (this->*read_entry_func_)(&start, &size);
+
+ Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
+ elf->Init();
+ if (!elf->valid()) {
+ // The data is not formatted in a way we understand, do not attempt
+ // to process any other entries.
+ entry_addr_ = 0;
+ delete elf;
+ return nullptr;
+ }
+ elf_list_.push_back(elf);
+
+ if (elf->IsValidPc(pc)) {
+ return elf;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 000000000..5d8120036
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+ pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+ // Create the maps.
+ maps_.reset(new unwindstack::LocalUpdatableMaps());
+ if (!maps_->Parse()) {
+ maps_.reset();
+ return false;
+ }
+
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+ return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+ for (const std::string& skip_library : skip_libraries_) {
+ if (skip_library == map_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+ pthread_rwlock_rdlock(&maps_rwlock_);
+ MapInfo* map_info = maps_->Find(pc);
+ pthread_rwlock_unlock(&maps_rwlock_);
+
+ if (map_info == nullptr) {
+ pthread_rwlock_wrlock(&maps_rwlock_);
+ // This is guaranteed not to invalidate any previous MapInfo objects so
+ // we don't need to worry about any MapInfo* values already in use.
+ if (maps_->Reparse()) {
+ map_info = maps_->Find(pc);
+ }
+ pthread_rwlock_unlock(&maps_rwlock_);
+ }
+
+ return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+ ArchEnum arch = regs->Arch();
+
+ size_t num_frames = 0;
+ bool adjust_pc = false;
+ while (true) {
+ uint64_t cur_pc = regs->pc();
+ uint64_t cur_sp = regs->sp();
+
+ MapInfo* map_info = GetMapInfo(cur_pc);
+ if (map_info == nullptr) {
+ break;
+ }
+
+ Elf* elf = map_info->GetElf(process_memory_, arch);
+ uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+ uint64_t step_pc = rel_pc;
+ uint64_t pc_adjustment;
+ if (adjust_pc) {
+ pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+ } else {
+ pc_adjustment = 0;
+ }
+ step_pc -= pc_adjustment;
+
+ bool finished = false;
+ if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
+ step_pc = rel_pc;
+ } else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished)) {
+ finished = true;
+ }
+
+ // Skip any locations that are within this library.
+ if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+ // Add frame information.
+ std::string func_name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+ func_name, func_offset);
+ } else {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+ }
+ num_frames++;
+ }
+
+ if (finished || frame_info->size() == max_frames ||
+ (cur_pc == regs->pc() && cur_sp == regs->sp())) {
+ break;
+ }
+ adjust_pc = true;
+ }
+ return num_frames != 0;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
new file mode 100644
index 000000000..436e23c28
--- /dev/null
+++ b/libunwindstack/Log.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+static bool g_print_to_stdout = false;
+
+void log_to_stdout(bool enable) {
+ g_print_to_stdout = enable;
+}
+
+// Send the data to the log.
+void log(uint8_t indent, const char* format, ...) {
+ std::string real_format;
+ if (indent > 0) {
+ real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
+ } else {
+ real_format = format;
+ }
+ va_list args;
+ va_start(args, format);
+ if (g_print_to_stdout) {
+ real_format += '\n';
+ vprintf(real_format.c_str(), args);
+ } else {
+ LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
+ }
+ va_end(args);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 000000000..31f314434
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "MemoryFileAtOffset.h"
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
+ // One last attempt, see if the previous map is read-only with the
+ // same name and stretches across this map.
+ if (prev_real_map == nullptr || prev_real_map->flags != PROT_READ) {
+ return false;
+ }
+
+ uint64_t map_size = end - prev_real_map->end;
+ if (!memory->Init(name, prev_real_map->offset, map_size)) {
+ return false;
+ }
+
+ uint64_t max_size;
+ if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+ return false;
+ }
+
+ if (!memory->Init(name, prev_real_map->offset, max_size)) {
+ return false;
+ }
+
+ elf_offset = offset - prev_real_map->offset;
+ elf_start_offset = prev_real_map->offset;
+ return true;
+}
+
+Memory* MapInfo::GetFileMemory() {
+ std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
+ if (offset == 0) {
+ if (memory->Init(name, 0)) {
+ return memory.release();
+ }
+ return nullptr;
+ }
+
+ // These are the possibilities when the offset is non-zero.
+ // - There is an elf file embedded in a file, and the offset is the
+ // the start of the elf in the file.
+ // - There is an elf file embedded in a file, and the offset is the
+ // the start of the executable part of the file. The actual start
+ // of the elf is in the read-only segment preceeding this map.
+ // - The whole file is an elf file, and the offset needs to be saved.
+ //
+ // Map in just the part of the file for the map. If this is not
+ // a valid elf, then reinit as if the whole file is an elf file.
+ // If the offset is a valid elf, then determine the size of the map
+ // and reinit to that size. This is needed because the dynamic linker
+ // only maps in a portion of the original elf, and never the symbol
+ // file data.
+ uint64_t map_size = end - start;
+ if (!memory->Init(name, offset, map_size)) {
+ return nullptr;
+ }
+
+ // Check if the start of this map is an embedded elf.
+ uint64_t max_size = 0;
+ if (Elf::GetInfo(memory.get(), &max_size)) {
+ elf_start_offset = offset;
+ if (max_size > map_size) {
+ if (memory->Init(name, offset, max_size)) {
+ return memory.release();
+ }
+ // Try to reinit using the default map_size.
+ if (memory->Init(name, offset, map_size)) {
+ return memory.release();
+ }
+ elf_start_offset = 0;
+ return nullptr;
+ }
+ return memory.release();
+ }
+
+ // No elf at offset, try to init as if the whole file is an elf.
+ if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
+ elf_offset = offset;
+ // Need to check how to set the elf start offset. If this map is not
+ // the r-x map of a r-- map, then use the real offset value. Otherwise,
+ // use 0.
+ if (prev_real_map == nullptr || prev_real_map->offset != 0 ||
+ prev_real_map->flags != PROT_READ || prev_real_map->name != name) {
+ elf_start_offset = offset;
+ }
+ return memory.release();
+ }
+
+ // See if the map previous to this one contains a read-only map
+ // that represents the real start of the elf data.
+ if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
+ return memory.release();
+ }
+
+ // Failed to find elf at start of file or at read-only map, return
+ // file object from the current map.
+ if (memory->Init(name, offset, map_size)) {
+ return memory.release();
+ }
+ return nullptr;
+}
+
+Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
+ if (end <= start) {
+ return nullptr;
+ }
+
+ elf_offset = 0;
+
+ // Fail on device maps.
+ if (flags & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
+ // First try and use the file associated with the info.
+ if (!name.empty()) {
+ Memory* memory = GetFileMemory();
+ if (memory != nullptr) {
+ return memory;
+ }
+ }
+
+ if (process_memory == nullptr) {
+ return nullptr;
+ }
+
+ // Need to verify that this elf is valid. It's possible that
+ // only part of the elf file to be mapped into memory is in the executable
+ // map. In this case, there will be another read-only map that includes the
+ // first part of the elf file. This is done if the linker rosegment
+ // option is used.
+ std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+ if (Elf::IsValidElf(memory.get())) {
+ memory_backed_elf = true;
+ return memory.release();
+ }
+
+ // Find the read-only map by looking at the previous map. The linker
+ // doesn't guarantee that this invariant will always be true. However,
+ // if that changes, there is likely something else that will change and
+ // break something.
+ if (offset == 0 || name.empty() || prev_real_map == nullptr || prev_real_map->name != name ||
+ prev_real_map->offset >= offset) {
+ return nullptr;
+ }
+
+ // Make sure that relative pc values are corrected properly.
+ elf_offset = offset - prev_real_map->offset;
+ // Use this as the elf start offset, otherwise, you always get offsets into
+ // the r-x section, which is not quite the right information.
+ elf_start_offset = prev_real_map->offset;
+
+ MemoryRanges* ranges = new MemoryRanges;
+ ranges->Insert(new MemoryRange(process_memory, prev_real_map->start,
+ prev_real_map->end - prev_real_map->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+ memory_backed_elf = true;
+ return ranges;
+}
+
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
+ {
+ // Make sure no other thread is trying to add the elf to this map.
+ std::lock_guard<std::mutex> guard(mutex_);
+
+ if (elf.get() != nullptr) {
+ return elf.get();
+ }
+
+ bool locked = false;
+ if (Elf::CachingEnabled() && !name.empty()) {
+ Elf::CacheLock();
+ locked = true;
+ if (Elf::CacheGet(this)) {
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+
+ Memory* memory = CreateMemory(process_memory);
+ if (locked) {
+ if (Elf::CacheAfterCreateMemory(this)) {
+ delete memory;
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+ elf.reset(new Elf(memory));
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ elf->Init();
+ if (elf->valid() && expected_arch != elf->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf->Invalidate();
+ }
+
+ if (locked) {
+ Elf::CacheAdd(this);
+ Elf::CacheUnlock();
+ }
+ }
+
+ if (!elf->valid()) {
+ elf_start_offset = offset;
+ } else if (prev_real_map != nullptr && elf_start_offset != offset &&
+ prev_real_map->offset == elf_start_offset && prev_real_map->name == name) {
+ // If there is a read-only map then a read-execute map that represents the
+ // same elf object, make sure the previous map is using the same elf
+ // object if it hasn't already been set.
+ std::lock_guard<std::mutex> guard(prev_real_map->mutex_);
+ if (prev_real_map->elf.get() == nullptr) {
+ prev_real_map->elf = elf;
+ prev_real_map->memory_backed_elf = memory_backed_elf;
+ }
+ }
+ return elf.get();
+}
+
+bool MapInfo::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+ {
+ // Make sure no other thread is trying to update this elf object.
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (elf == nullptr) {
+ return false;
+ }
+ }
+ // No longer need the lock, once the elf object is created, it is not deleted
+ // until this object is deleted.
+ return elf->GetFunctionName(addr, name, func_offset);
+}
+
+uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+ int64_t cur_load_bias = load_bias.load();
+ if (cur_load_bias != INT64_MAX) {
+ return cur_load_bias;
+ }
+
+ {
+ // Make sure no other thread is trying to add the elf to this map.
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (elf != nullptr) {
+ if (elf->valid()) {
+ cur_load_bias = elf->GetLoadBias();
+ load_bias = cur_load_bias;
+ return cur_load_bias;
+ } else {
+ load_bias = 0;
+ return 0;
+ }
+ }
+ }
+
+ // Call lightweight static function that will only read enough of the
+ // elf data to get the load bias.
+ std::unique_ptr<Memory> memory(CreateMemory(process_memory));
+ cur_load_bias = Elf::GetLoadBias(memory.get());
+ load_bias = cur_load_bias;
+ return cur_load_bias;
+}
+
+MapInfo::~MapInfo() {
+ uintptr_t id = build_id.load();
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ }
+}
+
+std::string MapInfo::GetBuildID() {
+ uintptr_t id = build_id.load();
+ if (id != 0) {
+ return *reinterpret_cast<std::string*>(id);
+ }
+
+ // No need to lock, at worst if multiple threads do this at the same
+ // time it should be detected and only one thread should win and
+ // save the data.
+ std::unique_ptr<std::string> cur_build_id(new std::string);
+
+ // Now need to see if the elf object exists.
+ // Make sure no other thread is trying to add the elf to this map.
+ mutex_.lock();
+ Elf* elf_obj = elf.get();
+ mutex_.unlock();
+ if (elf_obj != nullptr) {
+ *cur_build_id = elf_obj->GetBuildID();
+ } else {
+ // This will only work if we can get the file associated with this memory.
+ // If this is only available in memory, then the section name information
+ // is not present and we will not be able to find the build id info.
+ std::unique_ptr<Memory> memory(GetFileMemory());
+ if (memory != nullptr) {
+ *cur_build_id = Elf::GetBuildID(memory.get());
+ }
+ }
+
+ id = reinterpret_cast<uintptr_t>(cur_build_id.get());
+ uintptr_t expected_id = 0;
+ if (build_id.compare_exchange_weak(expected_id, id)) {
+ // Value saved, so make sure the memory is not freed.
+ cur_build_id.release();
+ }
+ return *reinterpret_cast<std::string*>(id);
+}
+
+std::string MapInfo::GetPrintableBuildID() {
+ std::string raw_build_id = GetBuildID();
+ if (raw_build_id.empty()) {
+ return "";
+ }
+ std::string printable_build_id;
+ for (const char& c : raw_build_id) {
+ // Use %hhx to avoid sign extension on abis that have signed chars.
+ printable_build_id += android::base::StringPrintf("%02hhx", c);
+ }
+ return printable_build_id;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
new file mode 100644
index 000000000..670d9046f
--- /dev/null
+++ b/libunwindstack/Maps.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
+
+#include <algorithm>
+#include <cctype>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+MapInfo* Maps::Find(uint64_t pc) {
+ if (maps_.empty()) {
+ return nullptr;
+ }
+ size_t first = 0;
+ size_t last = maps_.size();
+ while (first < last) {
+ size_t index = (first + last) / 2;
+ const auto& cur = maps_[index];
+ if (pc >= cur->start && pc < cur->end) {
+ return cur.get();
+ } else if (pc < cur->start) {
+ last = index;
+ } else {
+ first = index + 1;
+ }
+ }
+ return nullptr;
+}
+
+bool Maps::Parse() {
+ MapInfo* prev_map = nullptr;
+ MapInfo* prev_real_map = nullptr;
+ return android::procinfo::ReadMapFile(
+ GetMapsFile(),
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
+ // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+ if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
+ flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
+ }
+ maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+ prev_map = maps_.back().get();
+ if (!prev_map->IsBlank()) {
+ prev_real_map = prev_map;
+ }
+ });
+}
+
+void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const std::string& name, uint64_t load_bias) {
+ MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
+ MapInfo* prev_real_map = prev_map;
+ while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
+ prev_real_map = prev_real_map->prev_map;
+ }
+
+ auto map_info =
+ std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
+ map_info->load_bias = load_bias;
+ maps_.emplace_back(std::move(map_info));
+}
+
+void Maps::Sort() {
+ std::sort(maps_.begin(), maps_.end(),
+ [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+ return a->start < b->start; });
+
+ // Set the prev_map values on the info objects.
+ MapInfo* prev_map = nullptr;
+ MapInfo* prev_real_map = nullptr;
+ for (const auto& map_info : maps_) {
+ map_info->prev_map = prev_map;
+ map_info->prev_real_map = prev_real_map;
+ prev_map = map_info.get();
+ if (!prev_map->IsBlank()) {
+ prev_real_map = prev_map;
+ }
+ }
+}
+
+bool BufferMaps::Parse() {
+ std::string content(buffer_);
+ MapInfo* prev_map = nullptr;
+ MapInfo* prev_real_map = nullptr;
+ return android::procinfo::ReadMapFileContent(
+ &content[0],
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
+ // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+ if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
+ flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
+ }
+ maps_.emplace_back(new MapInfo(prev_map, prev_real_map, start, end, pgoff, flags, name));
+ prev_map = maps_.back().get();
+ if (!prev_map->IsBlank()) {
+ prev_real_map = prev_map;
+ }
+ });
+}
+
+const std::string RemoteMaps::GetMapsFile() const {
+ return "/proc/" + std::to_string(pid_) + "/maps";
+}
+
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+ return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+ // New maps will be added at the end without deleting the old ones.
+ size_t last_map_idx = maps_.size();
+ if (!Parse()) {
+ maps_.resize(last_map_idx);
+ return false;
+ }
+
+ size_t total_entries = maps_.size();
+ size_t search_map_idx = 0;
+ for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+ auto& new_map_info = maps_[new_map_idx];
+ uint64_t start = new_map_info->start;
+ uint64_t end = new_map_info->end;
+ uint64_t flags = new_map_info->flags;
+ std::string* name = &new_map_info->name;
+ for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+ auto& info = maps_[old_map_idx];
+ if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+ // No need to check
+ search_map_idx = old_map_idx + 1;
+ if (new_map_idx + 1 < maps_.size()) {
+ maps_[new_map_idx + 1]->prev_map = info.get();
+ maps_[new_map_idx + 1]->prev_real_map =
+ info->IsBlank() ? info->prev_real_map : info.get();
+ }
+ maps_[new_map_idx] = nullptr;
+ total_entries--;
+ break;
+ } else if (info->start > start) {
+ // Stop, there isn't going to be a match.
+ search_map_idx = old_map_idx;
+ break;
+ }
+
+ // Never delete these maps, they may be in use. The assumption is
+ // that there will only every be a handful of these so waiting
+ // to destroy them is not too expensive.
+ saved_maps_.emplace_back(std::move(info));
+ search_map_idx = old_map_idx + 1;
+ maps_[old_map_idx] = nullptr;
+ total_entries--;
+ }
+ if (search_map_idx >= last_map_idx) {
+ break;
+ }
+ }
+
+ // Now move out any of the maps that never were found.
+ for (size_t i = search_map_idx; i < last_map_idx; i++) {
+ saved_maps_.emplace_back(std::move(maps_[i]));
+ maps_[i] = nullptr;
+ total_entries--;
+ }
+
+ // Sort all of the values such that the nullptrs wind up at the end, then
+ // resize them away.
+ std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
+ if (a == nullptr) {
+ return false;
+ } else if (b == nullptr) {
+ return true;
+ }
+ return a->start < b->start;
+ });
+ maps_.resize(total_entries);
+
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
new file mode 100644
index 000000000..8de3d9808
--- /dev/null
+++ b/libunwindstack/Memory.cpp
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "MemoryBuffer.h"
+#include "MemoryCache.h"
+#include "MemoryFileAtOffset.h"
+#include "MemoryLocal.h"
+#include "MemoryOffline.h"
+#include "MemoryOfflineBuffer.h"
+#include "MemoryRange.h"
+#include "MemoryRemote.h"
+
+namespace unwindstack {
+
+static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
+
+ // Split up the remote read across page boundaries.
+ // From the manpage:
+ // A partial read/write may result if one of the remote_iov elements points to an invalid
+ // memory region in the remote process.
+ //
+ // Partial transfers apply at the granularity of iovec elements. These system calls won't
+ // perform a partial transfer that splits a single iovec element.
+ constexpr size_t kMaxIovecs = 64;
+ struct iovec src_iovs[kMaxIovecs];
+
+ uint64_t cur = remote_src;
+ size_t total_read = 0;
+ while (len > 0) {
+ struct iovec dst_iov = {
+ .iov_base = &reinterpret_cast<uint8_t*>(dst)[total_read], .iov_len = len,
+ };
+
+ size_t iovecs_used = 0;
+ while (len > 0) {
+ if (iovecs_used == kMaxIovecs) {
+ break;
+ }
+
+ // struct iovec uses void* for iov_base.
+ if (cur >= UINTPTR_MAX) {
+ errno = EFAULT;
+ return total_read;
+ }
+
+ src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
+
+ uintptr_t misalignment = cur & (getpagesize() - 1);
+ size_t iov_len = getpagesize() - misalignment;
+ iov_len = std::min(iov_len, len);
+
+ len -= iov_len;
+ if (__builtin_add_overflow(cur, iov_len, &cur)) {
+ errno = EFAULT;
+ return total_read;
+ }
+
+ src_iovs[iovecs_used].iov_len = iov_len;
+ ++iovecs_used;
+ }
+
+ ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
+ if (rc == -1) {
+ return total_read;
+ }
+ total_read += rc;
+ }
+ return total_read;
+}
+
+static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
+ // ptrace() returns -1 and sets errno when the operation fails.
+ // To disambiguate -1 from a valid result, we clear errno beforehand.
+ errno = 0;
+ *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+ if (*value == -1 && errno) {
+ return false;
+ }
+ return true;
+}
+
+static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, bytes, &max_size)) {
+ return 0;
+ }
+
+ size_t bytes_read = 0;
+ long data;
+ size_t align_bytes = addr & (sizeof(long) - 1);
+ if (align_bytes != 0) {
+ if (!PtraceReadLong(pid, addr & ~(sizeof(long) - 1), &data)) {
+ return 0;
+ }
+ size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
+ memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
+ addr += copy_bytes;
+ dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
+ bytes -= copy_bytes;
+ bytes_read += copy_bytes;
+ }
+
+ for (size_t i = 0; i < bytes / sizeof(long); i++) {
+ if (!PtraceReadLong(pid, addr, &data)) {
+ return bytes_read;
+ }
+ memcpy(dst, &data, sizeof(long));
+ dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
+ addr += sizeof(long);
+ bytes_read += sizeof(long);
+ }
+
+ size_t left_over = bytes & (sizeof(long) - 1);
+ if (left_over) {
+ if (!PtraceReadLong(pid, addr, &data)) {
+ return bytes_read;
+ }
+ memcpy(dst, &data, left_over);
+ bytes_read += left_over;
+ }
+ return bytes_read;
+}
+
+bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) {
+ size_t rc = Read(addr, dst, size);
+ return rc == size;
+}
+
+bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
+ string->clear();
+ uint64_t bytes_read = 0;
+ while (bytes_read < max_read) {
+ uint8_t value;
+ if (!ReadFully(addr, &value, sizeof(value))) {
+ return false;
+ }
+ if (value == '\0') {
+ return true;
+ }
+ string->push_back(value);
+ addr++;
+ bytes_read++;
+ }
+ return false;
+}
+
+std::unique_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64_t offset) {
+ auto memory = std::make_unique<MemoryFileAtOffset>();
+
+ if (memory->Init(path, offset)) {
+ return memory;
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
+ if (pid == getpid()) {
+ return std::shared_ptr<Memory>(new MemoryLocal());
+ }
+ return std::shared_ptr<Memory>(new MemoryRemote(pid));
+}
+
+std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
+ if (pid == getpid()) {
+ return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
+ }
+ return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
+}
+
+std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_t start,
+ uint64_t end) {
+ return std::shared_ptr<Memory>(new MemoryOfflineBuffer(data, start, end));
+}
+
+size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr >= size_) {
+ return 0;
+ }
+
+ size_t bytes_left = size_ - static_cast<size_t>(addr);
+ const unsigned char* actual_base = static_cast<const unsigned char*>(raw_) + addr;
+ size_t actual_len = std::min(bytes_left, size);
+
+ memcpy(dst, actual_base, actual_len);
+ return actual_len;
+}
+
+uint8_t* MemoryBuffer::GetPtr(size_t offset) {
+ if (offset < size_) {
+ return &raw_[offset];
+ }
+ return nullptr;
+}
+
+MemoryFileAtOffset::~MemoryFileAtOffset() {
+ Clear();
+}
+
+void MemoryFileAtOffset::Clear() {
+ if (data_) {
+ munmap(&data_[-offset_], size_ + offset_);
+ data_ = nullptr;
+ }
+}
+
+bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t size) {
+ // Clear out any previous data if it exists.
+ Clear();
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ return false;
+ }
+ struct stat buf;
+ if (fstat(fd, &buf) == -1) {
+ return false;
+ }
+ if (offset >= static_cast<uint64_t>(buf.st_size)) {
+ return false;
+ }
+
+ offset_ = offset & (getpagesize() - 1);
+ uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+ if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
+ offset > static_cast<uint64_t>(buf.st_size)) {
+ return false;
+ }
+
+ size_ = buf.st_size - aligned_offset;
+ uint64_t max_size;
+ if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
+ // Truncate the mapped size.
+ size_ = max_size;
+ }
+ void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
+ if (map == MAP_FAILED) {
+ return false;
+ }
+
+ data_ = &reinterpret_cast<uint8_t*>(map)[offset_];
+ size_ -= offset_;
+
+ return true;
+}
+
+size_t MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr >= size_) {
+ return 0;
+ }
+
+ size_t bytes_left = size_ - static_cast<size_t>(addr);
+ const unsigned char* actual_base = static_cast<const unsigned char*>(data_) + addr;
+ size_t actual_len = std::min(bytes_left, size);
+
+ memcpy(dst, actual_base, actual_len);
+ return actual_len;
+}
+
+size_t MemoryRemote::Read(uint64_t addr, void* dst, size_t size) {
+#if !defined(__LP64__)
+ // Cannot read an address greater than 32 bits in a 32 bit context.
+ if (addr > UINT32_MAX) {
+ return 0;
+ }
+#endif
+
+ size_t (*read_func)(pid_t, uint64_t, void*, size_t) =
+ reinterpret_cast<size_t (*)(pid_t, uint64_t, void*, size_t)>(read_redirect_func_.load());
+ if (read_func != nullptr) {
+ return read_func(pid_, addr, dst, size);
+ } else {
+ // Prefer process_vm_read, try it first. If it doesn't work, use the
+ // ptrace function. If at least one of them returns at least some data,
+ // set that as the permanent function to use.
+ // This assumes that if process_vm_read works once, it will continue
+ // to work.
+ size_t bytes = ProcessVmRead(pid_, addr, dst, size);
+ if (bytes > 0) {
+ read_redirect_func_ = reinterpret_cast<uintptr_t>(ProcessVmRead);
+ return bytes;
+ }
+ bytes = PtraceRead(pid_, addr, dst, size);
+ if (bytes > 0) {
+ read_redirect_func_ = reinterpret_cast<uintptr_t>(PtraceRead);
+ }
+ return bytes;
+ }
+}
+
+size_t MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
+ return ProcessVmRead(getpid(), addr, dst, size);
+}
+
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+ uint64_t offset)
+ : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
+
+size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr < offset_) {
+ return 0;
+ }
+
+ uint64_t read_offset = addr - offset_;
+ if (read_offset >= length_) {
+ return 0;
+ }
+
+ uint64_t read_length = std::min(static_cast<uint64_t>(size), length_ - read_offset);
+ uint64_t read_addr;
+ if (__builtin_add_overflow(read_offset, begin_, &read_addr)) {
+ return 0;
+ }
+
+ return memory_->Read(read_addr, dst, read_length);
+}
+
+void MemoryRanges::Insert(MemoryRange* memory) {
+ maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+ auto entry = maps_.upper_bound(addr);
+ if (entry != maps_.end()) {
+ return entry->second->Read(addr, dst, size);
+ }
+ return 0;
+}
+
+bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
+ auto memory_file = std::make_shared<MemoryFileAtOffset>();
+ if (!memory_file->Init(file, offset)) {
+ return false;
+ }
+
+ // The first uint64_t value is the start of memory.
+ uint64_t start;
+ if (!memory_file->ReadFully(0, &start, sizeof(start))) {
+ return false;
+ }
+
+ uint64_t size = memory_file->Size();
+ if (__builtin_sub_overflow(size, sizeof(start), &size)) {
+ return false;
+ }
+
+ memory_ = std::make_unique<MemoryRange>(memory_file, sizeof(start), size, start);
+ return true;
+}
+
+size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
+ if (!memory_) {
+ return 0;
+ }
+
+ return memory_->Read(addr, dst, size);
+}
+
+MemoryOfflineBuffer::MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end)
+ : data_(data), start_(start), end_(end) {}
+
+void MemoryOfflineBuffer::Reset(const uint8_t* data, uint64_t start, uint64_t end) {
+ data_ = data;
+ start_ = start;
+ end_ = end;
+}
+
+size_t MemoryOfflineBuffer::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr < start_ || addr >= end_) {
+ return 0;
+ }
+
+ size_t read_length = std::min(size, static_cast<size_t>(end_ - addr));
+ memcpy(dst, &data_[addr - start_], read_length);
+ return read_length;
+}
+
+MemoryOfflineParts::~MemoryOfflineParts() {
+ for (auto memory : memories_) {
+ delete memory;
+ }
+}
+
+size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) {
+ if (memories_.empty()) {
+ return 0;
+ }
+
+ // Do a read on each memory object, no support for reading across the
+ // different memory objects.
+ for (MemoryOffline* memory : memories_) {
+ size_t bytes = memory->Read(addr, dst, size);
+ if (bytes != 0) {
+ return bytes;
+ }
+ }
+ return 0;
+}
+
+size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
+ // Only bother caching and looking at the cache if this is a small read for now.
+ if (size > 64) {
+ return impl_->Read(addr, dst, size);
+ }
+
+ uint64_t addr_page = addr >> kCacheBits;
+ auto entry = cache_.find(addr_page);
+ uint8_t* cache_dst;
+ if (entry != cache_.end()) {
+ cache_dst = entry->second;
+ } else {
+ cache_dst = cache_[addr_page];
+ if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+ // Erase the entry.
+ cache_.erase(addr_page);
+ return impl_->Read(addr, dst, size);
+ }
+ }
+ size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
+ if (size <= max_read) {
+ memcpy(dst, &cache_dst[addr & kCacheMask], size);
+ return size;
+ }
+
+ // The read crossed into another cached entry, since a read can only cross
+ // into one extra cached page, duplicate the code rather than looping.
+ memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
+ dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
+ addr_page++;
+
+ entry = cache_.find(addr_page);
+ if (entry != cache_.end()) {
+ cache_dst = entry->second;
+ } else {
+ cache_dst = cache_[addr_page];
+ if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+ // Erase the entry.
+ cache_.erase(addr_page);
+ return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
+ }
+ }
+ memcpy(dst, cache_dst, size - max_read);
+ return size;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
new file mode 100644
index 000000000..a91e59f1e
--- /dev/null
+++ b/libunwindstack/MemoryBuffer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryBuffer : public Memory {
+ public:
+ MemoryBuffer() = default;
+ virtual ~MemoryBuffer() { free(raw_); }
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ uint8_t* GetPtr(size_t offset);
+
+ bool Resize(size_t size) {
+ raw_ = reinterpret_cast<uint8_t*>(realloc(raw_, size));
+ if (raw_ == nullptr) {
+ size_ = 0;
+ return false;
+ }
+ size_ = size;
+ return true;
+ }
+
+ uint64_t Size() { return size_; }
+
+ private:
+ uint8_t* raw_ = nullptr;
+ size_t size_ = 0;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_BUFFER_H
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
new file mode 100644
index 000000000..769d90746
--- /dev/null
+++ b/libunwindstack/MemoryCache.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_CACHE_H
+#define _LIBUNWINDSTACK_MEMORY_CACHE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryCache : public Memory {
+ public:
+ MemoryCache(Memory* memory) : impl_(memory) {}
+ virtual ~MemoryCache() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ void Clear() override { cache_.clear(); }
+
+ private:
+ constexpr static size_t kCacheBits = 12;
+ constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+ constexpr static size_t kCacheSize = 1 << kCacheBits;
+ std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+ std::unique_ptr<Memory> impl_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_CACHE_H
diff --git a/libunwindstack/MemoryFileAtOffset.h b/libunwindstack/MemoryFileAtOffset.h
new file mode 100644
index 000000000..d136eb44a
--- /dev/null
+++ b/libunwindstack/MemoryFileAtOffset.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryFileAtOffset : public Memory {
+ public:
+ MemoryFileAtOffset() = default;
+ virtual ~MemoryFileAtOffset();
+
+ bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ size_t Size() { return size_; }
+
+ void Clear() override;
+
+ protected:
+ size_t size_ = 0;
+ size_t offset_ = 0;
+ uint8_t* data_ = nullptr;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
new file mode 100644
index 000000000..29aaf128b
--- /dev/null
+++ b/libunwindstack/MemoryLocal.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_LOCAL_H
+#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryLocal : public Memory {
+ public:
+ MemoryLocal() = default;
+ virtual ~MemoryLocal() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_LOCAL_H
diff --git a/libunwindstack/MemoryOffline.h b/libunwindstack/MemoryOffline.h
new file mode 100644
index 000000000..789f1a268
--- /dev/null
+++ b/libunwindstack/MemoryOffline.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+class MemoryOffline : public Memory {
+ public:
+ MemoryOffline() = default;
+ virtual ~MemoryOffline() = default;
+
+ bool Init(const std::string& file, uint64_t offset);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::unique_ptr<MemoryRange> memory_;
+};
+
+class MemoryOfflineParts : public Memory {
+ public:
+ MemoryOfflineParts() = default;
+ virtual ~MemoryOfflineParts();
+
+ void Add(MemoryOffline* memory) { memories_.push_back(memory); }
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::vector<MemoryOffline*> memories_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_H
diff --git a/libunwindstack/MemoryOfflineBuffer.h b/libunwindstack/MemoryOfflineBuffer.h
new file mode 100644
index 000000000..64c49a1ca
--- /dev/null
+++ b/libunwindstack/MemoryOfflineBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryOfflineBuffer : public Memory {
+ public:
+ MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
+ virtual ~MemoryOfflineBuffer() = default;
+
+ void Reset(const uint8_t* data, uint64_t start, uint64_t end);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ const uint8_t* data_;
+ uint64_t start_;
+ uint64_t end_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
diff --git a/libunwindstack/MemoryRange.h b/libunwindstack/MemoryRange.h
new file mode 100644
index 000000000..3b4ab5c91
--- /dev/null
+++ b/libunwindstack/MemoryRange.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_RANGE_H
+#define _LIBUNWINDSTACK_MEMORY_RANGE_H
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// MemoryRange maps one address range onto another.
+// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
+// such that range.read(offset) is equivalent to underlying.read(src_begin).
+class MemoryRange : public Memory {
+ public:
+ MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+ uint64_t offset);
+ virtual ~MemoryRange() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ uint64_t offset() { return offset_; }
+ uint64_t length() { return length_; }
+
+ private:
+ std::shared_ptr<Memory> memory_;
+ uint64_t begin_;
+ uint64_t length_;
+ uint64_t offset_;
+};
+
+class MemoryRanges : public Memory {
+ public:
+ MemoryRanges() = default;
+ virtual ~MemoryRanges() = default;
+
+ void Insert(MemoryRange* memory);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_RANGE_H
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
new file mode 100644
index 000000000..db367d649
--- /dev/null
+++ b/libunwindstack/MemoryRemote.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_REMOTE_H
+#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <atomic>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryRemote : public Memory {
+ public:
+ MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
+ virtual ~MemoryRemote() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ pid_t pid() { return pid_; }
+
+ private:
+ pid_t pid_;
+ std::atomic_uintptr_t read_redirect_func_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_REMOTE_H
diff --git a/libunwindstack/OWNERS b/libunwindstack/OWNERS
new file mode 100644
index 000000000..6f7e4a3c4
--- /dev/null
+++ b/libunwindstack/OWNERS
@@ -0,0 +1 @@
+cferris@google.com
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
new file mode 100644
index 000000000..c7dec5259
--- /dev/null
+++ b/libunwindstack/Regs.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UserArm.h>
+#include <unwindstack/UserArm64.h>
+#include <unwindstack/UserMips.h>
+#include <unwindstack/UserMips64.h>
+#include <unwindstack/UserX86.h>
+#include <unwindstack/UserX86_64.h>
+
+namespace unwindstack {
+
+// The largest user structure.
+constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
+
+// This function assumes that reg_data is already aligned to a 64 bit value.
+// If not this could crash with an unaligned access.
+Regs* Regs::RemoteGet(pid_t pid) {
+ // Make the buffer large enough to contain the largest registers type.
+ std::vector<uint64_t> buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t));
+ struct iovec io;
+ io.iov_base = buffer.data();
+ io.iov_len = buffer.size() * sizeof(uint64_t);
+
+ if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, reinterpret_cast<void*>(&io)) == -1) {
+ return nullptr;
+ }
+
+ switch (io.iov_len) {
+ case sizeof(x86_user_regs):
+ return RegsX86::Read(buffer.data());
+ case sizeof(x86_64_user_regs):
+ return RegsX86_64::Read(buffer.data());
+ case sizeof(arm_user_regs):
+ return RegsArm::Read(buffer.data());
+ case sizeof(arm64_user_regs):
+ return RegsArm64::Read(buffer.data());
+ case sizeof(mips_user_regs):
+ return RegsMips::Read(buffer.data());
+ case sizeof(mips64_user_regs):
+ return RegsMips64::Read(buffer.data());
+ }
+ return nullptr;
+}
+
+Regs* Regs::CreateFromUcontext(ArchEnum arch, void* ucontext) {
+ switch (arch) {
+ case ARCH_X86:
+ return RegsX86::CreateFromUcontext(ucontext);
+ case ARCH_X86_64:
+ return RegsX86_64::CreateFromUcontext(ucontext);
+ case ARCH_ARM:
+ return RegsArm::CreateFromUcontext(ucontext);
+ case ARCH_ARM64:
+ return RegsArm64::CreateFromUcontext(ucontext);
+ case ARCH_MIPS:
+ return RegsMips::CreateFromUcontext(ucontext);
+ case ARCH_MIPS64:
+ return RegsMips64::CreateFromUcontext(ucontext);
+ case ARCH_UNKNOWN:
+ default:
+ return nullptr;
+ }
+}
+
+ArchEnum Regs::CurrentArch() {
+#if defined(__arm__)
+ return ARCH_ARM;
+#elif defined(__aarch64__)
+ return ARCH_ARM64;
+#elif defined(__i386__)
+ return ARCH_X86;
+#elif defined(__x86_64__)
+ return ARCH_X86_64;
+#elif defined(__mips__) && !defined(__LP64__)
+ return ARCH_MIPS;
+#elif defined(__mips__) && defined(__LP64__)
+ return ARCH_MIPS64;
+#else
+ abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+ Regs* regs;
+#if defined(__arm__)
+ regs = new RegsArm();
+#elif defined(__aarch64__)
+ regs = new RegsArm64();
+#elif defined(__i386__)
+ regs = new RegsX86();
+#elif defined(__x86_64__)
+ regs = new RegsX86_64();
+#elif defined(__mips__) && !defined(__LP64__)
+ regs = new RegsMips();
+#elif defined(__mips__) && defined(__LP64__)
+ regs = new RegsMips64();
+#else
+ abort();
+#endif
+ return regs;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
new file mode 100644
index 000000000..1b1f7ebbc
--- /dev/null
+++ b/libunwindstack/RegsArm.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/UcontextArm.h>
+#include <unwindstack/UserArm.h>
+
+namespace unwindstack {
+
+RegsArm::RegsArm() : RegsImpl<uint32_t>(ARM_REG_LAST, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
+
+ArchEnum RegsArm::Arch() {
+ return ARCH_ARM;
+}
+
+uint64_t RegsArm::pc() {
+ return regs_[ARM_REG_PC];
+}
+
+uint64_t RegsArm::sp() {
+ return regs_[ARM_REG_SP];
+}
+
+void RegsArm::set_pc(uint64_t pc) {
+ regs_[ARM_REG_PC] = pc;
+}
+
+void RegsArm::set_sp(uint64_t sp) {
+ regs_[ARM_REG_SP] = sp;
+}
+
+uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid()) {
+ return 2;
+ }
+
+ uint64_t load_bias = elf->GetLoadBias();
+ if (rel_pc < load_bias) {
+ if (rel_pc < 2) {
+ return 0;
+ }
+ return 2;
+ }
+ uint64_t adjusted_rel_pc = rel_pc - load_bias;
+ if (adjusted_rel_pc < 5) {
+ if (adjusted_rel_pc < 2) {
+ return 0;
+ }
+ return 2;
+ }
+
+ if (adjusted_rel_pc & 1) {
+ // This is a thumb instruction, it could be 2 or 4 bytes.
+ uint32_t value;
+ if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
+ (value & 0xe000f000) != 0xe000f000) {
+ return 2;
+ }
+ }
+ return 4;
+}
+
+bool RegsArm::SetPcFromReturnAddress(Memory*) {
+ uint32_t lr = regs_[ARM_REG_LR];
+ if (regs_[ARM_REG_PC] == lr) {
+ return false;
+ }
+
+ regs_[ARM_REG_PC] = lr;
+ return true;
+}
+
+void RegsArm::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("r0", regs_[ARM_REG_R0]);
+ fn("r1", regs_[ARM_REG_R1]);
+ fn("r2", regs_[ARM_REG_R2]);
+ fn("r3", regs_[ARM_REG_R3]);
+ fn("r4", regs_[ARM_REG_R4]);
+ fn("r5", regs_[ARM_REG_R5]);
+ fn("r6", regs_[ARM_REG_R6]);
+ fn("r7", regs_[ARM_REG_R7]);
+ fn("r8", regs_[ARM_REG_R8]);
+ fn("r9", regs_[ARM_REG_R9]);
+ fn("r10", regs_[ARM_REG_R10]);
+ fn("r11", regs_[ARM_REG_R11]);
+ fn("ip", regs_[ARM_REG_R12]);
+ fn("sp", regs_[ARM_REG_SP]);
+ fn("lr", regs_[ARM_REG_LR]);
+ fn("pc", regs_[ARM_REG_PC]);
+}
+
+Regs* RegsArm::Read(void* remote_data) {
+ arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
+
+ RegsArm* regs = new RegsArm();
+ memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
+ return regs;
+}
+
+Regs* RegsArm::CreateFromUcontext(void* ucontext) {
+ arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+ RegsArm* regs = new RegsArm();
+ memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+ return regs;
+}
+
+bool RegsArm::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
+ uint32_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
+ return false;
+ }
+
+ uint64_t offset = 0;
+ if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+ uint64_t sp = regs_[ARM_REG_SP];
+ // non-RT sigreturn call.
+ // __restore:
+ //
+ // Form 1 (arm):
+ // 0x77 0x70 mov r7, #0x77
+ // 0xa0 0xe3 svc 0x00000000
+ //
+ // Form 2 (arm):
+ // 0x77 0x00 0x90 0xef svc 0x00900077
+ //
+ // Form 3 (thumb):
+ // 0x77 0x27 movs r7, #77
+ // 0x00 0xdf svc 0
+ if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
+ return false;
+ }
+ if (data == 0x5ac3c35a) {
+ // SP + uc_mcontext offset + r0 offset.
+ offset = sp + 0x14 + 0xc;
+ } else {
+ // SP + r0 offset
+ offset = sp + 0xc;
+ }
+ } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+ uint64_t sp = regs_[ARM_REG_SP];
+ // RT sigreturn call.
+ // __restore_rt:
+ //
+ // Form 1 (arm):
+ // 0xad 0x70 mov r7, #0xad
+ // 0xa0 0xe3 svc 0x00000000
+ //
+ // Form 2 (arm):
+ // 0xad 0x00 0x90 0xef svc 0x009000ad
+ //
+ // Form 3 (thumb):
+ // 0xad 0x27 movs r7, #ad
+ // 0x00 0xdf svc 0
+ if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
+ return false;
+ }
+ if (data == sp + 8) {
+ // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+ offset = sp + 8 + 0x80 + 0x14 + 0xc;
+ } else {
+ // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+ offset = sp + 0x80 + 0x14 + 0xc;
+ }
+ }
+ if (offset == 0) {
+ return false;
+ }
+
+ if (!process_memory->ReadFully(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
+ return false;
+ }
+ return true;
+}
+
+Regs* RegsArm::Clone() {
+ return new RegsArm(*this);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
new file mode 100644
index 000000000..00b336737
--- /dev/null
+++ b/libunwindstack/RegsArm64.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/UcontextArm64.h>
+#include <unwindstack/UserArm64.h>
+
+namespace unwindstack {
+
+RegsArm64::RegsArm64()
+ : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+
+ArchEnum RegsArm64::Arch() {
+ return ARCH_ARM64;
+}
+
+uint64_t RegsArm64::pc() {
+ return regs_[ARM64_REG_PC];
+}
+
+uint64_t RegsArm64::sp() {
+ return regs_[ARM64_REG_SP];
+}
+
+void RegsArm64::set_pc(uint64_t pc) {
+ regs_[ARM64_REG_PC] = pc;
+}
+
+void RegsArm64::set_sp(uint64_t sp) {
+ regs_[ARM64_REG_SP] = sp;
+}
+
+uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc < 4) {
+ return 0;
+ }
+ return 4;
+}
+
+bool RegsArm64::SetPcFromReturnAddress(Memory*) {
+ uint64_t lr = regs_[ARM64_REG_LR];
+ if (regs_[ARM64_REG_PC] == lr) {
+ return false;
+ }
+
+ regs_[ARM64_REG_PC] = lr;
+ return true;
+}
+
+void RegsArm64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("x0", regs_[ARM64_REG_R0]);
+ fn("x1", regs_[ARM64_REG_R1]);
+ fn("x2", regs_[ARM64_REG_R2]);
+ fn("x3", regs_[ARM64_REG_R3]);
+ fn("x4", regs_[ARM64_REG_R4]);
+ fn("x5", regs_[ARM64_REG_R5]);
+ fn("x6", regs_[ARM64_REG_R6]);
+ fn("x7", regs_[ARM64_REG_R7]);
+ fn("x8", regs_[ARM64_REG_R8]);
+ fn("x9", regs_[ARM64_REG_R9]);
+ fn("x10", regs_[ARM64_REG_R10]);
+ fn("x11", regs_[ARM64_REG_R11]);
+ fn("x12", regs_[ARM64_REG_R12]);
+ fn("x13", regs_[ARM64_REG_R13]);
+ fn("x14", regs_[ARM64_REG_R14]);
+ fn("x15", regs_[ARM64_REG_R15]);
+ fn("x16", regs_[ARM64_REG_R16]);
+ fn("x17", regs_[ARM64_REG_R17]);
+ fn("x18", regs_[ARM64_REG_R18]);
+ fn("x19", regs_[ARM64_REG_R19]);
+ fn("x20", regs_[ARM64_REG_R20]);
+ fn("x21", regs_[ARM64_REG_R21]);
+ fn("x22", regs_[ARM64_REG_R22]);
+ fn("x23", regs_[ARM64_REG_R23]);
+ fn("x24", regs_[ARM64_REG_R24]);
+ fn("x25", regs_[ARM64_REG_R25]);
+ fn("x26", regs_[ARM64_REG_R26]);
+ fn("x27", regs_[ARM64_REG_R27]);
+ fn("x28", regs_[ARM64_REG_R28]);
+ fn("x29", regs_[ARM64_REG_R29]);
+ fn("lr", regs_[ARM64_REG_LR]);
+ fn("sp", regs_[ARM64_REG_SP]);
+ fn("pc", regs_[ARM64_REG_PC]);
+ fn("pst", regs_[ARM64_REG_PSTATE]);
+}
+
+Regs* RegsArm64::Read(void* remote_data) {
+ arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
+
+ RegsArm64* regs = new RegsArm64();
+ memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R30 + 1) * sizeof(uint64_t));
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ reg_data[ARM64_REG_SP] = user->sp;
+ reg_data[ARM64_REG_PC] = user->pc;
+ reg_data[ARM64_REG_PSTATE] = user->pstate;
+ return regs;
+}
+
+Regs* RegsArm64::CreateFromUcontext(void* ucontext) {
+ arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+ RegsArm64* regs = new RegsArm64();
+ memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+ return regs;
+}
+
+bool RegsArm64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
+ return false;
+ }
+
+ // Look for the kernel sigreturn function.
+ // __kernel_rt_sigreturn:
+ // 0xd2801168 mov x8, #0x8b
+ // 0xd4000001 svc #0x0
+ if (data != 0xd4000001d2801168ULL) {
+ return false;
+ }
+
+ // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
+ if (!process_memory->ReadFully(regs_[ARM64_REG_SP] + 0x80 + 0xb0 + 0x08, regs_.data(),
+ sizeof(uint64_t) * ARM64_REG_LAST)) {
+ return false;
+ }
+ return true;
+}
+
+Regs* RegsArm64::Clone() {
+ return new RegsArm64(*this);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
new file mode 100644
index 000000000..e445a9170
--- /dev/null
+++ b/libunwindstack/RegsInfo.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_INFO_H
+#define _LIBUNWINDSTACK_REGS_INFO_H
+
+#include <stdint.h>
+
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+struct RegsInfo {
+ static constexpr size_t MAX_REGISTERS = 64;
+
+ RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
+
+ RegsImpl<AddressType>* regs = nullptr;
+ uint64_t saved_reg_map = 0;
+ AddressType saved_regs[MAX_REGISTERS];
+
+ inline AddressType Get(uint32_t reg) {
+ if (IsSaved(reg)) {
+ return saved_regs[reg];
+ }
+ return (*regs)[reg];
+ }
+
+ inline AddressType* Save(uint32_t reg) {
+ if (reg >= MAX_REGISTERS) {
+ // This should never happen since all currently supported
+ // architectures have < 64 total registers.
+ abort();
+ }
+ saved_reg_map |= 1ULL << reg;
+ saved_regs[reg] = (*regs)[reg];
+ return &(*regs)[reg];
+ }
+
+ inline bool IsSaved(uint32_t reg) {
+ if (reg > MAX_REGISTERS) {
+ // This should never happen since all currently supported
+ // architectures have < 64 total registers.
+ abort();
+ }
+ return saved_reg_map & (1ULL << reg);
+ }
+
+ inline uint16_t Total() { return regs->total_regs(); }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_INFO_H
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
new file mode 100644
index 000000000..ebefe429b
--- /dev/null
+++ b/libunwindstack/RegsMips.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/UcontextMips.h>
+#include <unwindstack/UserMips.h>
+
+namespace unwindstack {
+
+RegsMips::RegsMips()
+ : RegsImpl<uint32_t>(MIPS_REG_LAST, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
+
+ArchEnum RegsMips::Arch() {
+ return ARCH_MIPS;
+}
+
+uint64_t RegsMips::pc() {
+ return regs_[MIPS_REG_PC];
+}
+
+uint64_t RegsMips::sp() {
+ return regs_[MIPS_REG_SP];
+}
+
+void RegsMips::set_pc(uint64_t pc) {
+ regs_[MIPS_REG_PC] = static_cast<uint32_t>(pc);
+}
+
+void RegsMips::set_sp(uint64_t sp) {
+ regs_[MIPS_REG_SP] = static_cast<uint32_t>(sp);
+}
+
+uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc < 8) {
+ return 0;
+ }
+ // For now, just assume no compact branches
+ return 8;
+}
+
+bool RegsMips::SetPcFromReturnAddress(Memory*) {
+ uint32_t ra = regs_[MIPS_REG_RA];
+ if (regs_[MIPS_REG_PC] == ra) {
+ return false;
+ }
+
+ regs_[MIPS_REG_PC] = ra;
+ return true;
+}
+
+void RegsMips::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("r0", regs_[MIPS_REG_R0]);
+ fn("r1", regs_[MIPS_REG_R1]);
+ fn("r2", regs_[MIPS_REG_R2]);
+ fn("r3", regs_[MIPS_REG_R3]);
+ fn("r4", regs_[MIPS_REG_R4]);
+ fn("r5", regs_[MIPS_REG_R5]);
+ fn("r6", regs_[MIPS_REG_R6]);
+ fn("r7", regs_[MIPS_REG_R7]);
+ fn("r8", regs_[MIPS_REG_R8]);
+ fn("r9", regs_[MIPS_REG_R9]);
+ fn("r10", regs_[MIPS_REG_R10]);
+ fn("r11", regs_[MIPS_REG_R11]);
+ fn("r12", regs_[MIPS_REG_R12]);
+ fn("r13", regs_[MIPS_REG_R13]);
+ fn("r14", regs_[MIPS_REG_R14]);
+ fn("r15", regs_[MIPS_REG_R15]);
+ fn("r16", regs_[MIPS_REG_R16]);
+ fn("r17", regs_[MIPS_REG_R17]);
+ fn("r18", regs_[MIPS_REG_R18]);
+ fn("r19", regs_[MIPS_REG_R19]);
+ fn("r20", regs_[MIPS_REG_R20]);
+ fn("r21", regs_[MIPS_REG_R21]);
+ fn("r22", regs_[MIPS_REG_R22]);
+ fn("r23", regs_[MIPS_REG_R23]);
+ fn("r24", regs_[MIPS_REG_R24]);
+ fn("r25", regs_[MIPS_REG_R25]);
+ fn("r26", regs_[MIPS_REG_R26]);
+ fn("r27", regs_[MIPS_REG_R27]);
+ fn("r28", regs_[MIPS_REG_R28]);
+ fn("sp", regs_[MIPS_REG_SP]);
+ fn("r30", regs_[MIPS_REG_R30]);
+ fn("ra", regs_[MIPS_REG_RA]);
+ fn("pc", regs_[MIPS_REG_PC]);
+}
+
+Regs* RegsMips::Read(void* remote_data) {
+ mips_user_regs* user = reinterpret_cast<mips_user_regs*>(remote_data);
+ RegsMips* regs = new RegsMips();
+ uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
+
+ memcpy(regs->RawData(), &user->regs[MIPS32_EF_R0], (MIPS_REG_R31 + 1) * sizeof(uint32_t));
+
+ reg_data[MIPS_REG_PC] = user->regs[MIPS32_EF_CP0_EPC];
+ return regs;
+}
+
+Regs* RegsMips::CreateFromUcontext(void* ucontext) {
+ mips_ucontext_t* mips_ucontext = reinterpret_cast<mips_ucontext_t*>(ucontext);
+
+ RegsMips* regs = new RegsMips();
+ // Copy 64 bit sc_regs over to 32 bit regs
+ for (int i = 0; i < 32; i++) {
+ (*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i];
+ }
+ (*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc;
+ return regs;
+}
+
+bool RegsMips::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ uint64_t offset = 0;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
+ return false;
+ }
+
+ // Look for the kernel sigreturn functions.
+ // __vdso_rt_sigreturn:
+ // 0x24021061 li v0, 0x1061
+ // 0x0000000c syscall
+ // __vdso_sigreturn:
+ // 0x24021017 li v0, 0x1017
+ // 0x0000000c syscall
+ if (data == 0x0000000c24021061ULL) {
+ // vdso_rt_sigreturn => read rt_sigframe
+ // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+ offset = 24 + 128 + 24 + 8;
+ } else if (data == 0x0000000c24021017LL) {
+ // vdso_sigreturn => read sigframe
+ // offset = sigcontext offset + sc_pc offset
+ offset = 24 + 8;
+ } else {
+ return false;
+ }
+
+ // read sc_pc and sc_regs[32] from stack
+ uint64_t values[MIPS_REG_LAST];
+ if (!process_memory->ReadFully(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
+ return false;
+ }
+
+ // Copy 64 bit sc_pc over to 32 bit regs_[MIPS_REG_PC]
+ regs_[MIPS_REG_PC] = values[0];
+
+ // Copy 64 bit sc_regs over to 32 bit regs
+ for (int i = 0; i < 32; i++) {
+ regs_[MIPS_REG_R0 + i] = values[1 + i];
+ }
+ return true;
+}
+
+Regs* RegsMips::Clone() {
+ return new RegsMips(*this);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
new file mode 100644
index 000000000..be2fd22f3
--- /dev/null
+++ b/libunwindstack/RegsMips64.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/UcontextMips64.h>
+#include <unwindstack/UserMips64.h>
+
+namespace unwindstack {
+
+RegsMips64::RegsMips64()
+ : RegsImpl<uint64_t>(MIPS64_REG_LAST, Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
+
+ArchEnum RegsMips64::Arch() {
+ return ARCH_MIPS64;
+}
+
+uint64_t RegsMips64::pc() {
+ return regs_[MIPS64_REG_PC];
+}
+
+uint64_t RegsMips64::sp() {
+ return regs_[MIPS64_REG_SP];
+}
+
+void RegsMips64::set_pc(uint64_t pc) {
+ regs_[MIPS64_REG_PC] = pc;
+}
+
+void RegsMips64::set_sp(uint64_t sp) {
+ regs_[MIPS64_REG_SP] = sp;
+}
+
+uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc < 8) {
+ return 0;
+ }
+ // For now, just assume no compact branches
+ return 8;
+}
+
+bool RegsMips64::SetPcFromReturnAddress(Memory*) {
+ uint64_t ra = regs_[MIPS64_REG_RA];
+ if (regs_[MIPS64_REG_PC] == ra) {
+ return false;
+ }
+
+ regs_[MIPS64_REG_PC] = ra;
+ return true;
+}
+
+void RegsMips64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("r0", regs_[MIPS64_REG_R0]);
+ fn("r1", regs_[MIPS64_REG_R1]);
+ fn("r2", regs_[MIPS64_REG_R2]);
+ fn("r3", regs_[MIPS64_REG_R3]);
+ fn("r4", regs_[MIPS64_REG_R4]);
+ fn("r5", regs_[MIPS64_REG_R5]);
+ fn("r6", regs_[MIPS64_REG_R6]);
+ fn("r7", regs_[MIPS64_REG_R7]);
+ fn("r8", regs_[MIPS64_REG_R8]);
+ fn("r9", regs_[MIPS64_REG_R9]);
+ fn("r10", regs_[MIPS64_REG_R10]);
+ fn("r11", regs_[MIPS64_REG_R11]);
+ fn("r12", regs_[MIPS64_REG_R12]);
+ fn("r13", regs_[MIPS64_REG_R13]);
+ fn("r14", regs_[MIPS64_REG_R14]);
+ fn("r15", regs_[MIPS64_REG_R15]);
+ fn("r16", regs_[MIPS64_REG_R16]);
+ fn("r17", regs_[MIPS64_REG_R17]);
+ fn("r18", regs_[MIPS64_REG_R18]);
+ fn("r19", regs_[MIPS64_REG_R19]);
+ fn("r20", regs_[MIPS64_REG_R20]);
+ fn("r21", regs_[MIPS64_REG_R21]);
+ fn("r22", regs_[MIPS64_REG_R22]);
+ fn("r23", regs_[MIPS64_REG_R23]);
+ fn("r24", regs_[MIPS64_REG_R24]);
+ fn("r25", regs_[MIPS64_REG_R25]);
+ fn("r26", regs_[MIPS64_REG_R26]);
+ fn("r27", regs_[MIPS64_REG_R27]);
+ fn("r28", regs_[MIPS64_REG_R28]);
+ fn("sp", regs_[MIPS64_REG_SP]);
+ fn("r30", regs_[MIPS64_REG_R30]);
+ fn("ra", regs_[MIPS64_REG_RA]);
+ fn("pc", regs_[MIPS64_REG_PC]);
+}
+
+Regs* RegsMips64::Read(void* remote_data) {
+ mips64_user_regs* user = reinterpret_cast<mips64_user_regs*>(remote_data);
+ RegsMips64* regs = new RegsMips64();
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+
+ memcpy(regs->RawData(), &user->regs[MIPS64_EF_R0], (MIPS64_REG_R31 + 1) * sizeof(uint64_t));
+
+ reg_data[MIPS64_REG_PC] = user->regs[MIPS64_EF_CP0_EPC];
+ return regs;
+}
+
+Regs* RegsMips64::CreateFromUcontext(void* ucontext) {
+ mips64_ucontext_t* mips64_ucontext = reinterpret_cast<mips64_ucontext_t*>(ucontext);
+
+ RegsMips64* regs = new RegsMips64();
+ // Copy 64 bit sc_regs over to 64 bit regs
+ memcpy(regs->RawData(), &mips64_ucontext->uc_mcontext.sc_regs[0], 32 * sizeof(uint64_t));
+ (*regs)[MIPS64_REG_PC] = mips64_ucontext->uc_mcontext.sc_pc;
+ return regs;
+}
+
+bool RegsMips64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(elf_offset, &data, sizeof(data))) {
+ return false;
+ }
+
+ // Look for the kernel sigreturn function.
+ // __vdso_rt_sigreturn:
+ // 0x2402145b li v0, 0x145b
+ // 0x0000000c syscall
+ if (data != 0x0000000c2402145bULL) {
+ return false;
+ }
+
+ // vdso_rt_sigreturn => read rt_sigframe
+ // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset
+ // read 64 bit sc_regs[32] from stack into 64 bit regs_
+ uint64_t sp = regs_[MIPS64_REG_SP];
+ if (!process_memory->Read(sp + 24 + 128 + 40, regs_.data(),
+ sizeof(uint64_t) * (MIPS64_REG_LAST - 1))) {
+ return false;
+ }
+
+ // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+ // read 64 bit sc_pc from stack into 64 bit regs_[MIPS64_REG_PC]
+ if (!process_memory->Read(sp + 24 + 128 + 40 + 576, &regs_[MIPS64_REG_PC], sizeof(uint64_t))) {
+ return false;
+ }
+ return true;
+}
+
+Regs* RegsMips64::Clone() {
+ return new RegsMips64(*this);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
new file mode 100644
index 000000000..5538fc0be
--- /dev/null
+++ b/libunwindstack/RegsX86.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/UcontextX86.h>
+#include <unwindstack/UserX86.h>
+
+namespace unwindstack {
+
+RegsX86::RegsX86() : RegsImpl<uint32_t>(X86_REG_LAST, Location(LOCATION_SP_OFFSET, -4)) {}
+
+ArchEnum RegsX86::Arch() {
+ return ARCH_X86;
+}
+
+uint64_t RegsX86::pc() {
+ return regs_[X86_REG_PC];
+}
+
+uint64_t RegsX86::sp() {
+ return regs_[X86_REG_SP];
+}
+
+void RegsX86::set_pc(uint64_t pc) {
+ regs_[X86_REG_PC] = static_cast<uint32_t>(pc);
+}
+
+void RegsX86::set_sp(uint64_t sp) {
+ regs_[X86_REG_SP] = static_cast<uint32_t>(sp);
+}
+
+uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
+ // Attempt to get the return address from the top of the stack.
+ uint32_t new_pc;
+ if (!process_memory->ReadFully(regs_[X86_REG_SP], &new_pc, sizeof(new_pc)) ||
+ new_pc == regs_[X86_REG_PC]) {
+ return false;
+ }
+
+ regs_[X86_REG_PC] = new_pc;
+ return true;
+}
+
+void RegsX86::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("eax", regs_[X86_REG_EAX]);
+ fn("ebx", regs_[X86_REG_EBX]);
+ fn("ecx", regs_[X86_REG_ECX]);
+ fn("edx", regs_[X86_REG_EDX]);
+ fn("ebp", regs_[X86_REG_EBP]);
+ fn("edi", regs_[X86_REG_EDI]);
+ fn("esi", regs_[X86_REG_ESI]);
+ fn("esp", regs_[X86_REG_ESP]);
+ fn("eip", regs_[X86_REG_EIP]);
+}
+
+Regs* RegsX86::Read(void* user_data) {
+ x86_user_regs* user = reinterpret_cast<x86_user_regs*>(user_data);
+
+ RegsX86* regs = new RegsX86();
+ (*regs)[X86_REG_EAX] = user->eax;
+ (*regs)[X86_REG_EBX] = user->ebx;
+ (*regs)[X86_REG_ECX] = user->ecx;
+ (*regs)[X86_REG_EDX] = user->edx;
+ (*regs)[X86_REG_EBP] = user->ebp;
+ (*regs)[X86_REG_EDI] = user->edi;
+ (*regs)[X86_REG_ESI] = user->esi;
+ (*regs)[X86_REG_ESP] = user->esp;
+ (*regs)[X86_REG_EIP] = user->eip;
+
+ return regs;
+}
+
+void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
+ // Put the registers in the expected order.
+ regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
+ regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
+ regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
+ regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
+ regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
+ regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
+ regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
+ regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
+ regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
+}
+
+Regs* RegsX86::CreateFromUcontext(void* ucontext) {
+ x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+ RegsX86* regs = new RegsX86();
+ regs->SetFromUcontext(x86_ucontext);
+ return regs;
+}
+
+bool RegsX86::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data))) {
+ return false;
+ }
+
+ if (data == 0x80cd00000077b858ULL) {
+ // Without SA_SIGINFO set, the return sequence is:
+ //
+ // __restore:
+ // 0x58 pop %eax
+ // 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax
+ // 0xcd 0x80 int 0x80
+ //
+ // SP points at arguments:
+ // int signum
+ // struct sigcontext (same format as mcontext)
+ struct x86_mcontext_t context;
+ if (!process_memory->ReadFully(regs_[X86_REG_SP] + 4, &context, sizeof(context))) {
+ return false;
+ }
+ regs_[X86_REG_EBP] = context.ebp;
+ regs_[X86_REG_ESP] = context.esp;
+ regs_[X86_REG_EBX] = context.ebx;
+ regs_[X86_REG_EDX] = context.edx;
+ regs_[X86_REG_ECX] = context.ecx;
+ regs_[X86_REG_EAX] = context.eax;
+ regs_[X86_REG_EIP] = context.eip;
+ return true;
+ } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
+ // With SA_SIGINFO set, the return sequence is:
+ //
+ // __restore_rt:
+ // 0xb8 0xad 0x00 0x00 0x00 movl 0xad,%eax
+ // 0xcd 0x80 int 0x80
+ //
+ // SP points at arguments:
+ // int signum
+ // siginfo*
+ // ucontext*
+
+ // Get the location of the sigcontext data.
+ uint32_t ptr;
+ if (!process_memory->ReadFully(regs_[X86_REG_SP] + 8, &ptr, sizeof(ptr))) {
+ return false;
+ }
+ // Only read the portion of the data structure we care about.
+ x86_ucontext_t x86_ucontext;
+ if (!process_memory->ReadFully(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
+ return false;
+ }
+ SetFromUcontext(&x86_ucontext);
+ return true;
+ }
+ return false;
+}
+
+Regs* RegsX86::Clone() {
+ return new RegsX86(*this);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
new file mode 100644
index 000000000..5b9aa58c4
--- /dev/null
+++ b/libunwindstack/RegsX86_64.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UcontextX86_64.h>
+#include <unwindstack/UserX86_64.h>
+
+namespace unwindstack {
+
+RegsX86_64::RegsX86_64() : RegsImpl<uint64_t>(X86_64_REG_LAST, Location(LOCATION_SP_OFFSET, -8)) {}
+
+ArchEnum RegsX86_64::Arch() {
+ return ARCH_X86_64;
+}
+
+uint64_t RegsX86_64::pc() {
+ return regs_[X86_64_REG_PC];
+}
+
+uint64_t RegsX86_64::sp() {
+ return regs_[X86_64_REG_SP];
+}
+
+void RegsX86_64::set_pc(uint64_t pc) {
+ regs_[X86_64_REG_PC] = pc;
+}
+
+void RegsX86_64::set_sp(uint64_t sp) {
+ regs_[X86_64_REG_SP] = sp;
+}
+
+uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
+ // Attempt to get the return address from the top of the stack.
+ uint64_t new_pc;
+ if (!process_memory->ReadFully(regs_[X86_64_REG_SP], &new_pc, sizeof(new_pc)) ||
+ new_pc == regs_[X86_64_REG_PC]) {
+ return false;
+ }
+
+ regs_[X86_64_REG_PC] = new_pc;
+ return true;
+}
+
+void RegsX86_64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("rax", regs_[X86_64_REG_RAX]);
+ fn("rbx", regs_[X86_64_REG_RBX]);
+ fn("rcx", regs_[X86_64_REG_RCX]);
+ fn("rdx", regs_[X86_64_REG_RDX]);
+ fn("r8", regs_[X86_64_REG_R8]);
+ fn("r9", regs_[X86_64_REG_R9]);
+ fn("r10", regs_[X86_64_REG_R10]);
+ fn("r11", regs_[X86_64_REG_R11]);
+ fn("r12", regs_[X86_64_REG_R12]);
+ fn("r13", regs_[X86_64_REG_R13]);
+ fn("r14", regs_[X86_64_REG_R14]);
+ fn("r15", regs_[X86_64_REG_R15]);
+ fn("rdi", regs_[X86_64_REG_RDI]);
+ fn("rsi", regs_[X86_64_REG_RSI]);
+ fn("rbp", regs_[X86_64_REG_RBP]);
+ fn("rsp", regs_[X86_64_REG_RSP]);
+ fn("rip", regs_[X86_64_REG_RIP]);
+}
+
+Regs* RegsX86_64::Read(void* remote_data) {
+ x86_64_user_regs* user = reinterpret_cast<x86_64_user_regs*>(remote_data);
+
+ RegsX86_64* regs = new RegsX86_64();
+ (*regs)[X86_64_REG_RAX] = user->rax;
+ (*regs)[X86_64_REG_RBX] = user->rbx;
+ (*regs)[X86_64_REG_RCX] = user->rcx;
+ (*regs)[X86_64_REG_RDX] = user->rdx;
+ (*regs)[X86_64_REG_R8] = user->r8;
+ (*regs)[X86_64_REG_R9] = user->r9;
+ (*regs)[X86_64_REG_R10] = user->r10;
+ (*regs)[X86_64_REG_R11] = user->r11;
+ (*regs)[X86_64_REG_R12] = user->r12;
+ (*regs)[X86_64_REG_R13] = user->r13;
+ (*regs)[X86_64_REG_R14] = user->r14;
+ (*regs)[X86_64_REG_R15] = user->r15;
+ (*regs)[X86_64_REG_RDI] = user->rdi;
+ (*regs)[X86_64_REG_RSI] = user->rsi;
+ (*regs)[X86_64_REG_RBP] = user->rbp;
+ (*regs)[X86_64_REG_RSP] = user->rsp;
+ (*regs)[X86_64_REG_RIP] = user->rip;
+
+ return regs;
+}
+
+void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
+ // R8-R15
+ memcpy(&regs_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+ // Rest of the registers.
+ regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
+ regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
+ regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
+ regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
+ regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
+ regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
+ regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
+ regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
+ regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
+}
+
+Regs* RegsX86_64::CreateFromUcontext(void* ucontext) {
+ x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+ RegsX86_64* regs = new RegsX86_64();
+ regs->SetFromUcontext(x86_64_ucontext);
+ return regs;
+}
+
+bool RegsX86_64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->ReadFully(elf_offset, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
+ return false;
+ }
+
+ uint16_t data2;
+ if (!elf_memory->ReadFully(elf_offset + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+ return false;
+ }
+
+ // __restore_rt:
+ // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax
+ // 0x0f 0x05 syscall
+ // 0x0f nopl 0x0($rax)
+
+ // Read the mcontext data from the stack.
+ // sp points to the ucontext data structure, read only the mcontext part.
+ x86_64_ucontext_t x86_64_ucontext;
+ if (!process_memory->ReadFully(regs_[X86_64_REG_SP] + 0x28, &x86_64_ucontext.uc_mcontext,
+ sizeof(x86_64_mcontext_t))) {
+ return false;
+ }
+ SetFromUcontext(&x86_64_ucontext);
+ return true;
+}
+
+Regs* RegsX86_64::Clone() {
+ return new RegsX86_64(*this);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 000000000..e3c15a298
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size)
+ : cur_offset_(offset),
+ offset_(offset),
+ end_(offset + size),
+ entry_size_(entry_size),
+ str_offset_(str_offset),
+ str_end_(str_offset_ + str_size) {}
+
+const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
+ // Binary search the table.
+ size_t first = 0;
+ size_t last = symbols_.size();
+ while (first < last) {
+ size_t current = first + (last - first) / 2;
+ const Info* info = &symbols_[current];
+ if (addr < info->start_offset) {
+ last = current;
+ } else if (addr < info->end_offset) {
+ return info;
+ } else {
+ first = current + 1;
+ }
+ }
+ return nullptr;
+}
+
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
+ if (symbols_.size() != 0) {
+ const Info* info = GetInfoFromCache(addr);
+ if (info) {
+ CHECK(addr >= info->start_offset && addr <= info->end_offset);
+ *func_offset = addr - info->start_offset;
+ return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+ }
+ }
+
+ bool symbol_added = false;
+ bool return_value = false;
+ while (cur_offset_ + entry_size_ <= end_) {
+ SymType entry;
+ if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
+ // Stop all processing, something looks like it is corrupted.
+ cur_offset_ = UINT64_MAX;
+ return false;
+ }
+ cur_offset_ += entry_size_;
+
+ if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
+ // Treat st_value as virtual address.
+ uint64_t start_offset = entry.st_value;
+ uint64_t end_offset = start_offset + entry.st_size;
+
+ // Cache the value.
+ symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
+ symbol_added = true;
+
+ if (addr >= start_offset && addr < end_offset) {
+ *func_offset = addr - start_offset;
+ uint64_t offset = str_offset_ + entry.st_name;
+ if (offset < str_end_) {
+ return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
+ }
+ break;
+ }
+ }
+ }
+
+ if (symbol_added) {
+ std::sort(symbols_.begin(), symbols_.end(),
+ [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+ }
+ return return_value;
+}
+
+template <typename SymType>
+bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
+ uint64_t cur_offset = offset_;
+ while (cur_offset + entry_size_ <= end_) {
+ SymType entry;
+ if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+ return false;
+ }
+ cur_offset += entry_size_;
+
+ if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
+ ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
+ uint64_t str_offset = str_offset_ + entry.st_name;
+ if (str_offset < str_end_) {
+ std::string symbol;
+ if (elf_memory->ReadString(str_offset, &symbol, str_end_ - str_offset) && symbol == name) {
+ *memory_address = entry.st_value;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// Instantiate all of the needed template functions.
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+
+template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
+template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
+} // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 000000000..7fcd067d3
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_SYMBOLS_H
+#define _LIBUNWINDSTACK_SYMBOLS_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declaration.
+class Memory;
+
+class Symbols {
+ struct Info {
+ Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
+ : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
+ uint64_t start_offset;
+ uint64_t end_offset;
+ uint64_t str_offset;
+ };
+
+ public:
+ Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size);
+ virtual ~Symbols() = default;
+
+ const Info* GetInfoFromCache(uint64_t addr);
+
+ template <typename SymType>
+ bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
+
+ template <typename SymType>
+ bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
+
+ void ClearCache() {
+ symbols_.clear();
+ cur_offset_ = offset_;
+ }
+
+ private:
+ uint64_t cur_offset_;
+ uint64_t offset_;
+ uint64_t end_;
+ uint64_t entry_size_;
+ uint64_t str_offset_;
+ uint64_t str_end_;
+
+ std::vector<Info> symbols_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
new file mode 100644
index 000000000..909f8976c
--- /dev/null
+++ b/libunwindstack/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "libunwindstack_unit_test"
+ },
+ {
+ "name": "CtsSimpleperfTestCases"
+ }
+ ]
+}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
new file mode 100644
index 000000000..1bb031947
--- /dev/null
+++ b/libunwindstack/Unwinder.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <elf.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Unwinder.h>
+
+#include <unwindstack/DexFiles.h>
+
+// Use the demangler from libc++.
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
+
+namespace unwindstack {
+
+// Inject extra 'virtual' frame that represents the dex pc data.
+// The dex pc is a magic register defined in the Mterp interpreter,
+// and thus it will be restored/observed in the frame after it.
+// Adding the dex frame first here will create something like:
+// #7 pc 0015fa20 core.vdex java.util.Arrays.binarySearch+8
+// #8 pc 006b1ba1 libartd.so ExecuteMterpImpl+14625
+// #9 pc 0039a1ef libartd.so art::interpreter::Execute+719
+void Unwinder::FillInDexFrame() {
+ size_t frame_num = frames_.size();
+ frames_.resize(frame_num + 1);
+ FrameData* frame = &frames_.at(frame_num);
+ frame->num = frame_num;
+
+ uint64_t dex_pc = regs_->dex_pc();
+ frame->pc = dex_pc;
+ frame->sp = regs_->sp();
+
+ MapInfo* info = maps_->Find(dex_pc);
+ if (info != nullptr) {
+ frame->map_start = info->start;
+ frame->map_end = info->end;
+ // Since this is a dex file frame, the elf_start_offset is not set
+ // by any of the normal code paths. Use the offset of the map since
+ // that matches the actual offset.
+ frame->map_elf_start_offset = info->offset;
+ frame->map_exact_offset = info->offset;
+ frame->map_load_bias = info->load_bias;
+ frame->map_flags = info->flags;
+ if (resolve_names_) {
+ frame->map_name = info->name;
+ }
+ frame->rel_pc = dex_pc - info->start;
+ } else {
+ frame->rel_pc = dex_pc;
+ return;
+ }
+
+ if (!resolve_names_) {
+ return;
+ }
+
+#if defined(DEXFILE_SUPPORT)
+ if (dex_files_ == nullptr) {
+ return;
+ }
+
+ dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
+ &frame->function_offset);
+#endif
+}
+
+FrameData* Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc,
+ uint64_t pc_adjustment) {
+ size_t frame_num = frames_.size();
+ frames_.resize(frame_num + 1);
+ FrameData* frame = &frames_.at(frame_num);
+ frame->num = frame_num;
+ frame->sp = regs_->sp();
+ frame->rel_pc = rel_pc - pc_adjustment;
+ frame->pc = regs_->pc() - pc_adjustment;
+
+ if (map_info == nullptr) {
+ // Nothing else to update.
+ return nullptr;
+ }
+
+ if (resolve_names_) {
+ frame->map_name = map_info->name;
+ if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
+ std::string soname = elf->GetSoname();
+ if (!soname.empty()) {
+ frame->map_name += '!' + soname;
+ }
+ }
+ }
+ frame->map_elf_start_offset = map_info->elf_start_offset;
+ frame->map_exact_offset = map_info->offset;
+ frame->map_start = map_info->start;
+ frame->map_end = map_info->end;
+ frame->map_flags = map_info->flags;
+ frame->map_load_bias = elf->GetLoadBias();
+ return frame;
+}
+
+static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
+ std::string& map_name) {
+ if (map_suffixes_to_ignore == nullptr) {
+ return false;
+ }
+ auto pos = map_name.find_last_of('.');
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ return std::find(map_suffixes_to_ignore->begin(), map_suffixes_to_ignore->end(),
+ map_name.substr(pos + 1)) != map_suffixes_to_ignore->end();
+}
+
+void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+ const std::vector<std::string>* map_suffixes_to_ignore) {
+ frames_.clear();
+ last_error_.code = ERROR_NONE;
+ last_error_.address = 0;
+ elf_from_memory_not_file_ = false;
+
+ ArchEnum arch = regs_->Arch();
+
+ bool return_address_attempt = false;
+ bool adjust_pc = false;
+ for (; frames_.size() < max_frames_;) {
+ uint64_t cur_pc = regs_->pc();
+ uint64_t cur_sp = regs_->sp();
+
+ MapInfo* map_info = maps_->Find(regs_->pc());
+ uint64_t pc_adjustment = 0;
+ uint64_t step_pc;
+ uint64_t rel_pc;
+ Elf* elf;
+ if (map_info == nullptr) {
+ step_pc = regs_->pc();
+ rel_pc = step_pc;
+ last_error_.code = ERROR_INVALID_MAP;
+ } else {
+ if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
+ break;
+ }
+ elf = map_info->GetElf(process_memory_, arch);
+ // If this elf is memory backed, and there is a valid file, then set
+ // an indicator that we couldn't open the file.
+ if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
+ map_info->name[0] != '[' && !android::base::StartsWith(map_info->name, "/memfd:")) {
+ elf_from_memory_not_file_ = true;
+ }
+ step_pc = regs_->pc();
+ rel_pc = elf->GetRelPc(step_pc, map_info);
+ // Everyone except elf data in gdb jit debug maps uses the relative pc.
+ if (!(map_info->flags & MAPS_FLAGS_JIT_SYMFILE_MAP)) {
+ step_pc = rel_pc;
+ }
+ if (adjust_pc) {
+ pc_adjustment = regs_->GetPcAdjustment(rel_pc, elf);
+ } else {
+ pc_adjustment = 0;
+ }
+ step_pc -= pc_adjustment;
+
+ // If the pc is in an invalid elf file, try and get an Elf object
+ // using the jit debug information.
+ if (!elf->valid() && jit_debug_ != nullptr) {
+ uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
+ Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
+ if (jit_elf != nullptr) {
+ // The jit debug information requires a non relative adjusted pc.
+ step_pc = adjusted_jit_pc;
+ elf = jit_elf;
+ }
+ }
+ }
+
+ FrameData* frame = nullptr;
+ if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+ std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
+ basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
+ if (regs_->dex_pc() != 0) {
+ // Add a frame to represent the dex file.
+ FillInDexFrame();
+ // Clear the dex pc so that we don't repeat this frame later.
+ regs_->set_dex_pc(0);
+
+ // Make sure there is enough room for the real frame.
+ if (frames_.size() == max_frames_) {
+ last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+ break;
+ }
+ }
+
+ frame = FillInFrame(map_info, elf, rel_pc, pc_adjustment);
+
+ // Once a frame is added, stop skipping frames.
+ initial_map_names_to_skip = nullptr;
+ }
+ adjust_pc = true;
+
+ bool stepped = false;
+ bool in_device_map = false;
+ bool finished = false;
+ if (map_info != nullptr) {
+ if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+ // Do not stop here, fall through in case we are
+ // in the speculative unwind path and need to remove
+ // some of the speculative frames.
+ in_device_map = true;
+ } else {
+ MapInfo* sp_info = maps_->Find(regs_->sp());
+ if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+ // Do not stop here, fall through in case we are
+ // in the speculative unwind path and need to remove
+ // some of the speculative frames.
+ in_device_map = true;
+ } else {
+ if (elf->StepIfSignalHandler(rel_pc, regs_, process_memory_.get())) {
+ stepped = true;
+ if (frame != nullptr) {
+ // Need to adjust the relative pc because the signal handler
+ // pc should not be adjusted.
+ frame->rel_pc = rel_pc;
+ frame->pc += pc_adjustment;
+ step_pc = rel_pc;
+ }
+ } else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished)) {
+ stepped = true;
+ }
+ elf->GetLastError(&last_error_);
+ }
+ }
+ }
+
+ if (frame != nullptr) {
+ if (!resolve_names_ ||
+ !elf->GetFunctionName(step_pc, &frame->function_name, &frame->function_offset)) {
+ frame->function_name = "";
+ frame->function_offset = 0;
+ }
+ }
+
+ if (finished) {
+ break;
+ }
+
+ if (!stepped) {
+ if (return_address_attempt) {
+ // Only remove the speculative frame if there are more than two frames
+ // or the pc in the first frame is in a valid map.
+ // This allows for a case where the code jumps into the middle of
+ // nowhere, but there is no other unwind information after that.
+ if (frames_.size() > 2 || (frames_.size() > 0 && maps_->Find(frames_[0].pc) != nullptr)) {
+ // Remove the speculative frame.
+ frames_.pop_back();
+ }
+ break;
+ } else if (in_device_map) {
+ // Do not attempt any other unwinding, pc or sp is in a device
+ // map.
+ break;
+ } else {
+ // Steping didn't work, try this secondary method.
+ if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
+ break;
+ }
+ return_address_attempt = true;
+ }
+ } else {
+ return_address_attempt = false;
+ if (max_frames_ == frames_.size()) {
+ last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+ }
+ }
+
+ // If the pc and sp didn't change, then consider everything stopped.
+ if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
+ last_error_.code = ERROR_REPEATED_FRAME;
+ break;
+ }
+ }
+}
+
+std::string Unwinder::FormatFrame(const FrameData& frame) const {
+ std::string data;
+ if (regs_->Is32Bit()) {
+ data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
+ } else {
+ data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
+ }
+
+ if (frame.map_start == frame.map_end) {
+ // No valid map associated with this frame.
+ data += " <unknown>";
+ } else if (!frame.map_name.empty()) {
+ data += " " + frame.map_name;
+ } else {
+ data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
+ }
+
+ if (frame.map_elf_start_offset != 0) {
+ data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+ }
+
+ if (!frame.function_name.empty()) {
+ char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name == nullptr) {
+ data += " (" + frame.function_name;
+ } else {
+ data += " (";
+ data += demangled_name;
+ free(demangled_name);
+ }
+ if (frame.function_offset != 0) {
+ data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ data += ')';
+ }
+
+ MapInfo* map_info = maps_->Find(frame.map_start);
+ if (map_info != nullptr && display_build_id_) {
+ std::string build_id = map_info->GetPrintableBuildID();
+ if (!build_id.empty()) {
+ data += " (BuildId: " + build_id + ')';
+ }
+ }
+ return data;
+}
+
+std::string Unwinder::FormatFrame(size_t frame_num) const {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrame(frames_[frame_num]);
+}
+
+void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
+ jit_debug->SetArch(arch);
+ jit_debug_ = jit_debug;
+}
+
+void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
+ dex_files->SetArch(arch);
+ dex_files_ = dex_files;
+}
+
+bool UnwinderFromPid::Init(ArchEnum arch) {
+ if (pid_ == getpid()) {
+ maps_ptr_.reset(new LocalMaps());
+ } else {
+ maps_ptr_.reset(new RemoteMaps(pid_));
+ }
+ if (!maps_ptr_->Parse()) {
+ return false;
+ }
+ maps_ = maps_ptr_.get();
+
+ process_memory_ = Memory::CreateProcessMemoryCached(pid_);
+
+ jit_debug_ptr_.reset(new JitDebug(process_memory_));
+ jit_debug_ = jit_debug_ptr_.get();
+ SetJitDebug(jit_debug_, arch);
+#if defined(DEXFILE_SUPPORT)
+ dex_files_ptr_.reset(new DexFiles(process_memory_));
+ dex_files_ = dex_files_ptr_.get();
+ SetDexFiles(dex_files_, arch);
+#endif
+
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
new file mode 100644
index 000000000..de9137aa4
--- /dev/null
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+
+#include <benchmark/benchmark.h>
+
+#include <android-base/strings.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+ unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory);
+ unwinder.Unwind();
+ return unwinder.NumFrames();
+}
+
+size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call6(process_memory, maps);
+}
+
+size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call5(process_memory, maps);
+}
+
+size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call4(process_memory, maps);
+}
+
+size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call3(process_memory, maps);
+}
+
+size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call2(process_memory, maps);
+}
+
+static void BM_uncached_unwind(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(Call1(process_memory, &maps));
+ }
+}
+BENCHMARK(BM_uncached_unwind);
+
+static void BM_cached_unwind(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(Call1(process_memory, &maps));
+ }
+}
+BENCHMARK(BM_cached_unwind);
+
+static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
+ unwindstack::MapInfo** build_id_map_info) {
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ return;
+ }
+
+ // Find the libc.so share library and use that for benchmark purposes.
+ *build_id_map_info = nullptr;
+ for (auto& map_info : maps) {
+ if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+ *build_id_map_info = map_info.get();
+ break;
+ }
+ }
+
+ if (*build_id_map_info == nullptr) {
+ state.SkipWithError("Failed to find a map with a BuildID.");
+ }
+}
+
+static void BM_get_build_id_from_elf(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ Initialize(state, maps, &build_id_map_info);
+
+ unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+ unwindstack::Regs::CurrentArch());
+ if (!elf->valid()) {
+ state.SkipWithError("Cannot get valid elf from map.");
+ }
+
+ for (auto _ : state) {
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_get_build_id_from_elf);
+
+static void BM_get_build_id_from_file(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ Initialize(state, maps, &build_id_map_info);
+
+ for (auto _ : state) {
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_get_build_id_from_file);
+
+BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
new file mode 100644
index 000000000..67a9640d6
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DEX_FILES_H
+#define _LIBUNWINDSTACK_DEX_FILES_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class DexFile;
+class Maps;
+struct MapInfo;
+enum ArchEnum : uint8_t;
+
+class DexFiles : public Global {
+ public:
+ explicit DexFiles(std::shared_ptr<Memory>& memory);
+ DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+ virtual ~DexFiles();
+
+ DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
+
+ void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
+ uint64_t* method_offset);
+
+ private:
+ void Init(Maps* maps);
+
+ bool GetAddr(size_t index, uint64_t* addr);
+
+ uint64_t ReadEntryPtr32(uint64_t addr);
+
+ uint64_t ReadEntryPtr64(uint64_t addr);
+
+ bool ReadEntry32();
+
+ bool ReadEntry64();
+
+ bool ReadVariableData(uint64_t ptr_offset) override;
+
+ void ProcessArch() override;
+
+ std::mutex lock_;
+ bool initialized_ = false;
+ std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_;
+
+ uint64_t entry_addr_ = 0;
+ uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
+ bool (DexFiles::*read_entry_func_)() = nullptr;
+ std::vector<uint64_t> addrs_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DEX_FILES_H
diff --git a/libunwindstack/include/unwindstack/DwarfError.h b/libunwindstack/include/unwindstack/DwarfError.h
new file mode 100644
index 000000000..763e2cbfe
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfError.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum DwarfErrorCode : uint8_t {
+ DWARF_ERROR_NONE,
+ DWARF_ERROR_MEMORY_INVALID,
+ DWARF_ERROR_ILLEGAL_VALUE,
+ DWARF_ERROR_ILLEGAL_STATE,
+ DWARF_ERROR_STACK_INDEX_NOT_VALID,
+ DWARF_ERROR_NOT_IMPLEMENTED,
+ DWARF_ERROR_TOO_MANY_ITERATIONS,
+ DWARF_ERROR_CFA_NOT_DEFINED,
+ DWARF_ERROR_UNSUPPORTED_VERSION,
+ DWARF_ERROR_NO_FDES,
+};
+
+struct DwarfErrorData {
+ DwarfErrorCode code;
+ uint64_t address;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
new file mode 100644
index 000000000..3d50ccf36
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_LOCATION_H
+#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+namespace unwindstack {
+
+struct DwarfCie;
+
+enum DwarfLocationEnum : uint8_t {
+ DWARF_LOCATION_INVALID = 0,
+ DWARF_LOCATION_UNDEFINED,
+ DWARF_LOCATION_OFFSET,
+ DWARF_LOCATION_VAL_OFFSET,
+ DWARF_LOCATION_REGISTER,
+ DWARF_LOCATION_EXPRESSION,
+ DWARF_LOCATION_VAL_EXPRESSION,
+};
+
+struct DwarfLocation {
+ DwarfLocationEnum type;
+ uint64_t values[2];
+};
+
+struct DwarfLocations : public std::unordered_map<uint32_t, DwarfLocation> {
+ const DwarfCie* cie;
+ // The range of PCs where the locations are valid (end is exclusive).
+ uint64_t pc_start = 0;
+ uint64_t pc_end = 0;
+};
+typedef DwarfLocations dwarf_loc_regs_t;
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
new file mode 100644
index 000000000..c45699a9e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+ DwarfMemory(Memory* memory) : memory_(memory) {}
+ virtual ~DwarfMemory() = default;
+
+ bool ReadBytes(void* dst, size_t num_bytes);
+
+ template <typename SignedType>
+ bool ReadSigned(uint64_t* value);
+
+ bool ReadULEB128(uint64_t* value);
+
+ bool ReadSLEB128(int64_t* value);
+
+ template <typename AddressType>
+ size_t GetEncodedSize(uint8_t encoding);
+
+ bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+ template <typename AddressType>
+ bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+ uint64_t cur_offset() { return cur_offset_; }
+ void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+ void set_pc_offset(int64_t offset) { pc_offset_ = offset; }
+ void clear_pc_offset() { pc_offset_ = INT64_MAX; }
+
+ void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+ void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+ void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+ void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+ Memory* memory_;
+ uint64_t cur_offset_ = 0;
+
+ int64_t pc_offset_ = INT64_MAX;
+ uint64_t data_offset_ = static_cast<uint64_t>(-1);
+ uint64_t func_offset_ = static_cast<uint64_t>(-1);
+ uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
new file mode 100644
index 000000000..c244749c1
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <map>
+#include <unordered_map>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+class Regs;
+template <typename AddressType>
+struct RegsInfo;
+
+class DwarfSection {
+ public:
+ DwarfSection(Memory* memory);
+ virtual ~DwarfSection() = default;
+
+ class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+ public:
+ iterator(DwarfSection* section, size_t index) : index_(index) {
+ section->GetFdes(&fdes_);
+ if (index_ == static_cast<size_t>(-1)) {
+ index_ = fdes_.size();
+ }
+ }
+
+ iterator& operator++() {
+ index_++;
+ return *this;
+ }
+ iterator& operator++(int increment) {
+ index_ += increment;
+ return *this;
+ }
+ iterator& operator--() {
+ index_--;
+ return *this;
+ }
+ iterator& operator--(int decrement) {
+ index_ -= decrement;
+ return *this;
+ }
+
+ bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+ bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+ const DwarfFde* operator*() {
+ if (index_ > fdes_.size()) return nullptr;
+ return fdes_[index_];
+ }
+
+ private:
+ std::vector<const DwarfFde*> fdes_;
+ size_t index_ = 0;
+ };
+
+ iterator begin() { return iterator(this, 0); }
+ iterator end() { return iterator(this, static_cast<size_t>(-1)); }
+
+ DwarfErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ virtual bool Init(uint64_t offset, uint64_t size, int64_t section_bias) = 0;
+
+ virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
+
+ virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
+
+ virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
+
+ virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
+
+ virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+ virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+ virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+ virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
+
+ protected:
+ DwarfMemory memory_;
+ DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
+
+ uint32_t cie32_value_ = 0;
+ uint64_t cie64_value_ = 0;
+
+ std::unordered_map<uint64_t, DwarfFde> fde_entries_;
+ std::unordered_map<uint64_t, DwarfCie> cie_entries_;
+ std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+ std::map<uint64_t, dwarf_loc_regs_t> loc_regs_; // Single row indexed by pc_end.
+};
+
+template <typename AddressType>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+ DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+ virtual ~DwarfSectionImpl() = default;
+
+ bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
+
+ const DwarfCie* GetCieFromOffset(uint64_t offset);
+
+ const DwarfFde* GetFdeFromOffset(uint64_t offset);
+
+ const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+ void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
+
+ bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+ Regs* regs, bool* finished) override;
+
+ bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+
+ bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
+
+ protected:
+ bool GetNextCieOrFde(const DwarfFde** fde_entry);
+
+ bool FillInCieHeader(DwarfCie* cie);
+
+ bool FillInCie(DwarfCie* cie);
+
+ bool FillInFdeHeader(DwarfFde* fde);
+
+ bool FillInFde(DwarfFde* fde);
+
+ bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
+ RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
+
+ void InsertFde(const DwarfFde* fde);
+
+ int64_t section_bias_ = 0;
+ uint64_t entries_offset_ = 0;
+ uint64_t entries_end_ = 0;
+ uint64_t next_entries_offset_ = 0;
+ uint64_t pc_offset_ = 0;
+
+ std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/include/unwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
new file mode 100644
index 000000000..4b481f082
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_STRUCTS_H
+#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace unwindstack {
+
+struct DwarfCie {
+ uint8_t version = 0;
+ uint8_t fde_address_encoding = 0;
+ uint8_t lsda_encoding = 0;
+ uint8_t segment_size = 0;
+ std::vector<char> augmentation_string;
+ uint64_t personality_handler = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t code_alignment_factor = 0;
+ int64_t data_alignment_factor = 0;
+ uint64_t return_address_register = 0;
+};
+
+struct DwarfFde {
+ uint64_t cie_offset = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t pc_start = 0;
+ uint64_t pc_end = 0;
+ uint64_t lsda_address = 0;
+ const DwarfCie* cie = nullptr;
+};
+
+constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
new file mode 100644
index 000000000..472ed925c
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ELF_H
+#define _LIBUNWINDSTACK_ELF_H
+
+#include <stddef.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+namespace unwindstack {
+
+// Forward declaration.
+struct MapInfo;
+class Regs;
+
+enum ArchEnum : uint8_t {
+ ARCH_UNKNOWN = 0,
+ ARCH_ARM,
+ ARCH_ARM64,
+ ARCH_X86,
+ ARCH_X86_64,
+ ARCH_MIPS,
+ ARCH_MIPS64,
+};
+
+class Elf {
+ public:
+ Elf(Memory* memory) : memory_(memory) {}
+ virtual ~Elf() = default;
+
+ bool Init();
+
+ void InitGnuDebugdata();
+
+ void Invalidate();
+
+ std::string GetSoname();
+
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+ bool GetGlobalVariableOffset(const std::string& name, uint64_t* memory_offset);
+
+ uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+
+ bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
+
+ ElfInterface* CreateInterfaceFromMemory(Memory* memory);
+
+ std::string GetBuildID();
+
+ int64_t GetLoadBias() { return load_bias_; }
+
+ bool IsValidPc(uint64_t pc);
+
+ void GetLastError(ErrorData* data);
+ ErrorCode GetLastErrorCode();
+ uint64_t GetLastErrorAddress();
+
+ bool valid() { return valid_; }
+
+ uint32_t machine_type() { return machine_type_; }
+
+ uint8_t class_type() { return class_type_; }
+
+ ArchEnum arch() { return arch_; }
+
+ Memory* memory() { return memory_.get(); }
+
+ ElfInterface* interface() { return interface_.get(); }
+
+ ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
+ static bool IsValidElf(Memory* memory);
+
+ static bool GetInfo(Memory* memory, uint64_t* size);
+
+ static int64_t GetLoadBias(Memory* memory);
+
+ static std::string GetBuildID(Memory* memory);
+
+ static void SetCachingEnabled(bool enable);
+ static bool CachingEnabled() { return cache_enabled_; }
+
+ static void CacheLock();
+ static void CacheUnlock();
+ static void CacheAdd(MapInfo* info);
+ static bool CacheGet(MapInfo* info);
+ static bool CacheAfterCreateMemory(MapInfo* info);
+
+ protected:
+ bool valid_ = false;
+ int64_t load_bias_ = 0;
+ std::unique_ptr<ElfInterface> interface_;
+ std::unique_ptr<Memory> memory_;
+ uint32_t machine_type_;
+ uint8_t class_type_;
+ ArchEnum arch_;
+ // Protect calls that can modify internal state of the interface object.
+ std::mutex lock_;
+
+ std::unique_ptr<Memory> gnu_debugdata_memory_;
+ std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
+
+ static bool cache_enabled_;
+ static std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* cache_;
+ static std::mutex* cache_lock_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
new file mode 100644
index 000000000..0c39b238b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_H
+#define _LIBUNWINDSTACK_ELF_INTERFACE_H
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Error.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+class Regs;
+class Symbols;
+
+struct LoadInfo {
+ uint64_t offset;
+ uint64_t table_offset;
+ size_t table_size;
+};
+
+enum : uint8_t {
+ SONAME_UNKNOWN = 0,
+ SONAME_VALID,
+ SONAME_INVALID,
+};
+
+class ElfInterface {
+ public:
+ ElfInterface(Memory* memory) : memory_(memory) {}
+ virtual ~ElfInterface();
+
+ virtual bool Init(int64_t* load_bias) = 0;
+
+ virtual void InitHeaders() = 0;
+
+ virtual std::string GetSoname() = 0;
+
+ virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
+
+ virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
+
+ virtual std::string GetBuildID() = 0;
+
+ virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
+
+ virtual bool IsValidPc(uint64_t pc);
+
+ Memory* CreateGnuDebugdataMemory();
+
+ Memory* memory() { return memory_; }
+
+ const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
+
+ void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
+
+ uint64_t dynamic_offset() { return dynamic_offset_; }
+ uint64_t dynamic_vaddr_start() { return dynamic_vaddr_start_; }
+ uint64_t dynamic_vaddr_end() { return dynamic_vaddr_end_; }
+ uint64_t data_offset() { return data_offset_; }
+ uint64_t data_vaddr_start() { return data_vaddr_start_; }
+ uint64_t data_vaddr_end() { return data_vaddr_end_; }
+ uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
+ int64_t eh_frame_hdr_section_bias() { return eh_frame_hdr_section_bias_; }
+ uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
+ uint64_t eh_frame_offset() { return eh_frame_offset_; }
+ int64_t eh_frame_section_bias() { return eh_frame_section_bias_; }
+ uint64_t eh_frame_size() { return eh_frame_size_; }
+ uint64_t debug_frame_offset() { return debug_frame_offset_; }
+ int64_t debug_frame_section_bias() { return debug_frame_section_bias_; }
+ uint64_t debug_frame_size() { return debug_frame_size_; }
+ uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
+ uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
+ uint64_t gnu_build_id_offset() { return gnu_build_id_offset_; }
+ uint64_t gnu_build_id_size() { return gnu_build_id_size_; }
+
+ DwarfSection* eh_frame() { return eh_frame_.get(); }
+ DwarfSection* debug_frame() { return debug_frame_.get(); }
+
+ const ErrorData& last_error() { return last_error_; }
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ template <typename EhdrType, typename PhdrType>
+ static int64_t GetLoadBias(Memory* memory);
+
+ template <typename EhdrType, typename ShdrType, typename NhdrType>
+ static std::string ReadBuildIDFromMemory(Memory* memory);
+
+ protected:
+ template <typename AddressType>
+ void InitHeadersWithTemplate();
+
+ template <typename EhdrType, typename PhdrType, typename ShdrType>
+ bool ReadAllHeaders(int64_t* load_bias);
+
+ template <typename EhdrType, typename PhdrType>
+ void ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias);
+
+ template <typename EhdrType, typename ShdrType>
+ void ReadSectionHeaders(const EhdrType& ehdr);
+
+ template <typename DynType>
+ std::string GetSonameWithTemplate();
+
+ template <typename SymType>
+ bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+ template <typename SymType>
+ bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
+
+ virtual void HandleUnknownType(uint32_t, uint64_t, uint64_t) {}
+
+ template <typename EhdrType>
+ static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+
+ template <typename NhdrType>
+ std::string ReadBuildID();
+
+ Memory* memory_;
+ std::unordered_map<uint64_t, LoadInfo> pt_loads_;
+
+ // Stored elf data.
+ uint64_t dynamic_offset_ = 0;
+ uint64_t dynamic_vaddr_start_ = 0;
+ uint64_t dynamic_vaddr_end_ = 0;
+
+ uint64_t data_offset_ = 0;
+ uint64_t data_vaddr_start_ = 0;
+ uint64_t data_vaddr_end_ = 0;
+
+ uint64_t eh_frame_hdr_offset_ = 0;
+ int64_t eh_frame_hdr_section_bias_ = 0;
+ uint64_t eh_frame_hdr_size_ = 0;
+
+ uint64_t eh_frame_offset_ = 0;
+ int64_t eh_frame_section_bias_ = 0;
+ uint64_t eh_frame_size_ = 0;
+
+ uint64_t debug_frame_offset_ = 0;
+ int64_t debug_frame_section_bias_ = 0;
+ uint64_t debug_frame_size_ = 0;
+
+ uint64_t gnu_debugdata_offset_ = 0;
+ uint64_t gnu_debugdata_size_ = 0;
+
+ uint64_t gnu_build_id_offset_ = 0;
+ uint64_t gnu_build_id_size_ = 0;
+
+ uint8_t soname_type_ = SONAME_UNKNOWN;
+ std::string soname_;
+
+ ErrorData last_error_{ERROR_NONE, 0};
+
+ std::unique_ptr<DwarfSection> eh_frame_;
+ std::unique_ptr<DwarfSection> debug_frame_;
+ // The Elf object owns the gnu_debugdata interface object.
+ ElfInterface* gnu_debugdata_interface_ = nullptr;
+
+ std::vector<Symbols*> symbols_;
+ std::vector<std::pair<uint64_t, uint64_t>> strtabs_;
+};
+
+class ElfInterface32 : public ElfInterface {
+ public:
+ ElfInterface32(Memory* memory) : ElfInterface(memory) {}
+ virtual ~ElfInterface32() = default;
+
+ bool Init(int64_t* load_bias) override {
+ return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
+ }
+
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
+
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
+
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
+ }
+
+ bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+ return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
+ }
+
+ std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf32_Nhdr>(); }
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
+ }
+};
+
+class ElfInterface64 : public ElfInterface {
+ public:
+ ElfInterface64(Memory* memory) : ElfInterface(memory) {}
+ virtual ~ElfInterface64() = default;
+
+ bool Init(int64_t* load_bias) override {
+ return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
+ }
+
+ void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
+
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
+
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+ return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
+ }
+
+ bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+ return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
+ }
+
+ std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf64_Nhdr>(); }
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
+ }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
new file mode 100644
index 000000000..72ec4547f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum ErrorCode : uint8_t {
+ ERROR_NONE, // No error.
+ ERROR_MEMORY_INVALID, // Memory read failed.
+ ERROR_UNWIND_INFO, // Unable to use unwind information to unwind.
+ ERROR_UNSUPPORTED, // Encountered unsupported feature.
+ ERROR_INVALID_MAP, // Unwind in an invalid map.
+ ERROR_MAX_FRAMES_EXCEEDED, // The number of frames exceed the total allowed.
+ ERROR_REPEATED_FRAME, // The last frame has the same pc/sp as the next.
+ ERROR_INVALID_ELF, // Unwind in an invalid elf.
+};
+
+struct ErrorData {
+ ErrorCode code;
+ uint64_t address; // Only valid when code is ERROR_MEMORY_INVALID.
+ // Indicates the failing address.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
new file mode 100644
index 000000000..b9bb141b1
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_GLOBAL_H
+#define _LIBUNWINDSTACK_GLOBAL_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Maps;
+struct MapInfo;
+
+class Global {
+ public:
+ explicit Global(std::shared_ptr<Memory>& memory);
+ Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+ virtual ~Global() = default;
+
+ void SetArch(ArchEnum arch);
+
+ ArchEnum arch() { return arch_; }
+
+ protected:
+ bool Searchable(const std::string& name);
+ void FindAndReadVariable(Maps* maps, const char* variable);
+
+ virtual bool ReadVariableData(uint64_t offset) = 0;
+
+ virtual void ProcessArch() = 0;
+
+ ArchEnum arch_ = ARCH_UNKNOWN;
+
+ std::shared_ptr<Memory> memory_;
+ std::vector<std::string> search_libs_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_GLOBAL_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
new file mode 100644
index 000000000..8b7b4b590
--- /dev/null
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_JIT_DEBUG_H
+#define _LIBUNWINDSTACK_JIT_DEBUG_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Global.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+class Maps;
+enum ArchEnum : uint8_t;
+
+class JitDebug : public Global {
+ public:
+ explicit JitDebug(std::shared_ptr<Memory>& memory);
+ JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+ virtual ~JitDebug();
+
+ Elf* GetElf(Maps* maps, uint64_t pc);
+
+ private:
+ void Init(Maps* maps);
+
+ uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
+ uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
+
+ uint64_t ReadDescriptor32(uint64_t);
+ uint64_t ReadDescriptor64(uint64_t);
+
+ uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
+ uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
+ uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+
+ bool ReadVariableData(uint64_t ptr_offset) override;
+
+ void ProcessArch() override;
+
+ uint64_t entry_addr_ = 0;
+ bool initialized_ = false;
+ std::vector<Elf*> elf_list_;
+
+ std::mutex lock_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_JIT_DEBUG_H
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 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+ LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+ uint64_t function_offset)
+ : map_info(map_info),
+ pc(pc),
+ rel_pc(rel_pc),
+ function_name(function_name),
+ function_offset(function_offset) {}
+
+ MapInfo* map_info;
+ uint64_t pc;
+ uint64_t rel_pc;
+ std::string function_name;
+ uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+ LocalUnwinder() = default;
+ LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+ ~LocalUnwinder() = default;
+
+ bool Init();
+
+ bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+ bool ShouldSkipLibrary(const std::string& map_name);
+
+ MapInfo* GetMapInfo(uint64_t pc);
+
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+ pthread_rwlock_t maps_rwlock_;
+ std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+ std::shared_ptr<Memory> process_memory_;
+ std::vector<std::string> skip_libraries_;
+ ErrorData last_error_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
new file mode 100644
index 000000000..aa1219c26
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_LOG_H
+#define _LIBUNWINDSTACK_LOG_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+void log_to_stdout(bool enable);
+void log(uint8_t indent, const char* format, ...);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/include/unwindstack/MachineArm.h b/libunwindstack/include/unwindstack/MachineArm.h
new file mode 100644
index 000000000..3f902b194
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineArm.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_ARM_H
+#define _LIBUNWINDSTACK_MACHINE_ARM_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum ArmReg : uint16_t {
+ ARM_REG_R0 = 0,
+ ARM_REG_R1,
+ ARM_REG_R2,
+ ARM_REG_R3,
+ ARM_REG_R4,
+ ARM_REG_R5,
+ ARM_REG_R6,
+ ARM_REG_R7,
+ ARM_REG_R8,
+ ARM_REG_R9,
+ ARM_REG_R10,
+ ARM_REG_R11,
+ ARM_REG_R12,
+ ARM_REG_R13,
+ ARM_REG_R14,
+ ARM_REG_R15,
+ ARM_REG_LAST,
+
+ ARM_REG_SP = ARM_REG_R13,
+ ARM_REG_LR = ARM_REG_R14,
+ ARM_REG_PC = ARM_REG_R15,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_ARM_H
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
new file mode 100644
index 000000000..e95333510
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_ARM64_H
+#define _LIBUNWINDSTACK_MACHINE_ARM64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Arm64Reg : uint16_t {
+ ARM64_REG_R0 = 0,
+ ARM64_REG_R1,
+ ARM64_REG_R2,
+ ARM64_REG_R3,
+ ARM64_REG_R4,
+ ARM64_REG_R5,
+ ARM64_REG_R6,
+ ARM64_REG_R7,
+ ARM64_REG_R8,
+ ARM64_REG_R9,
+ ARM64_REG_R10,
+ ARM64_REG_R11,
+ ARM64_REG_R12,
+ ARM64_REG_R13,
+ ARM64_REG_R14,
+ ARM64_REG_R15,
+ ARM64_REG_R16,
+ ARM64_REG_R17,
+ ARM64_REG_R18,
+ ARM64_REG_R19,
+ ARM64_REG_R20,
+ ARM64_REG_R21,
+ ARM64_REG_R22,
+ ARM64_REG_R23,
+ ARM64_REG_R24,
+ ARM64_REG_R25,
+ ARM64_REG_R26,
+ ARM64_REG_R27,
+ ARM64_REG_R28,
+ ARM64_REG_R29,
+ ARM64_REG_R30,
+ ARM64_REG_R31,
+ ARM64_REG_PC,
+ ARM64_REG_PSTATE,
+ ARM64_REG_LAST,
+
+ ARM64_REG_SP = ARM64_REG_R31,
+ ARM64_REG_LR = ARM64_REG_R30,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_ARM64_H
diff --git a/libunwindstack/include/unwindstack/MachineMips.h b/libunwindstack/include/unwindstack/MachineMips.h
new file mode 100644
index 000000000..2dfb1e940
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineMips.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_MIPS_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum MipsReg : uint16_t {
+ MIPS_REG_R0 = 0,
+ MIPS_REG_R1,
+ MIPS_REG_R2,
+ MIPS_REG_R3,
+ MIPS_REG_R4,
+ MIPS_REG_R5,
+ MIPS_REG_R6,
+ MIPS_REG_R7,
+ MIPS_REG_R8,
+ MIPS_REG_R9,
+ MIPS_REG_R10,
+ MIPS_REG_R11,
+ MIPS_REG_R12,
+ MIPS_REG_R13,
+ MIPS_REG_R14,
+ MIPS_REG_R15,
+ MIPS_REG_R16,
+ MIPS_REG_R17,
+ MIPS_REG_R18,
+ MIPS_REG_R19,
+ MIPS_REG_R20,
+ MIPS_REG_R21,
+ MIPS_REG_R22,
+ MIPS_REG_R23,
+ MIPS_REG_R24,
+ MIPS_REG_R25,
+ MIPS_REG_R26,
+ MIPS_REG_R27,
+ MIPS_REG_R28,
+ MIPS_REG_R29,
+ MIPS_REG_R30,
+ MIPS_REG_R31,
+ MIPS_REG_PC,
+ MIPS_REG_LAST,
+
+ MIPS_REG_SP = MIPS_REG_R29,
+ MIPS_REG_RA = MIPS_REG_R31,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_MIPS_H \ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineMips64.h b/libunwindstack/include/unwindstack/MachineMips64.h
new file mode 100644
index 000000000..34addf24d
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineMips64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_MIPS64_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Mips64Reg : uint16_t {
+ MIPS64_REG_R0 = 0,
+ MIPS64_REG_R1,
+ MIPS64_REG_R2,
+ MIPS64_REG_R3,
+ MIPS64_REG_R4,
+ MIPS64_REG_R5,
+ MIPS64_REG_R6,
+ MIPS64_REG_R7,
+ MIPS64_REG_R8,
+ MIPS64_REG_R9,
+ MIPS64_REG_R10,
+ MIPS64_REG_R11,
+ MIPS64_REG_R12,
+ MIPS64_REG_R13,
+ MIPS64_REG_R14,
+ MIPS64_REG_R15,
+ MIPS64_REG_R16,
+ MIPS64_REG_R17,
+ MIPS64_REG_R18,
+ MIPS64_REG_R19,
+ MIPS64_REG_R20,
+ MIPS64_REG_R21,
+ MIPS64_REG_R22,
+ MIPS64_REG_R23,
+ MIPS64_REG_R24,
+ MIPS64_REG_R25,
+ MIPS64_REG_R26,
+ MIPS64_REG_R27,
+ MIPS64_REG_R28,
+ MIPS64_REG_R29,
+ MIPS64_REG_R30,
+ MIPS64_REG_R31,
+ MIPS64_REG_PC,
+ MIPS64_REG_LAST,
+
+ MIPS64_REG_SP = MIPS64_REG_R29,
+ MIPS64_REG_RA = MIPS64_REG_R31,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_MIPS64_H \ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineX86.h b/libunwindstack/include/unwindstack/MachineX86.h
new file mode 100644
index 000000000..02adb98c3
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineX86.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_X86_H
+#define _LIBUNWINDSTACK_MACHINE_X86_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
+enum X86Reg : uint16_t {
+ X86_REG_EAX = 0,
+ X86_REG_ECX = 1,
+ X86_REG_EDX = 2,
+ X86_REG_EBX = 3,
+ X86_REG_ESP = 4,
+ X86_REG_EBP = 5,
+ X86_REG_ESI = 6,
+ X86_REG_EDI = 7,
+ X86_REG_EIP = 8,
+ X86_REG_EFL = 9,
+ X86_REG_CS = 10,
+ X86_REG_SS = 11,
+ X86_REG_DS = 12,
+ X86_REG_ES = 13,
+ X86_REG_FS = 14,
+ X86_REG_GS = 15,
+ X86_REG_LAST,
+
+ X86_REG_SP = X86_REG_ESP,
+ X86_REG_PC = X86_REG_EIP,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_X86_H
diff --git a/libunwindstack/include/unwindstack/MachineX86_64.h b/libunwindstack/include/unwindstack/MachineX86_64.h
new file mode 100644
index 000000000..af33fea80
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineX86_64.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MACHINE_X86_64_H
+#define _LIBUNWINDSTACK_MACHINE_X86_64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
+enum X86_64Reg : uint16_t {
+ X86_64_REG_RAX = 0,
+ X86_64_REG_RDX = 1,
+ X86_64_REG_RCX = 2,
+ X86_64_REG_RBX = 3,
+ X86_64_REG_RSI = 4,
+ X86_64_REG_RDI = 5,
+ X86_64_REG_RBP = 6,
+ X86_64_REG_RSP = 7,
+ X86_64_REG_R8 = 8,
+ X86_64_REG_R9 = 9,
+ X86_64_REG_R10 = 10,
+ X86_64_REG_R11 = 11,
+ X86_64_REG_R12 = 12,
+ X86_64_REG_R13 = 13,
+ X86_64_REG_R14 = 14,
+ X86_64_REG_R15 = 15,
+ X86_64_REG_RIP = 16,
+ X86_64_REG_LAST,
+
+ X86_64_REG_SP = X86_64_REG_RSP,
+ X86_64_REG_PC = X86_64_REG_RIP,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_X86_64_H
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
new file mode 100644
index 000000000..052e79fe1
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MAP_INFO_H
+#define _LIBUNWINDSTACK_MAP_INFO_H
+
+#include <stdint.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <unwindstack/Elf.h>
+
+namespace unwindstack {
+
+class MemoryFileAtOffset;
+
+struct MapInfo {
+ MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t flags, const char* name)
+ : start(start),
+ end(end),
+ offset(offset),
+ flags(flags),
+ name(name),
+ prev_map(prev_map),
+ prev_real_map(prev_real_map),
+ load_bias(INT64_MAX),
+ build_id(0) {}
+ MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t flags, const std::string& name)
+ : start(start),
+ end(end),
+ offset(offset),
+ flags(flags),
+ name(name),
+ prev_map(prev_map),
+ prev_real_map(prev_real_map),
+ load_bias(INT64_MAX),
+ build_id(0) {}
+ ~MapInfo();
+
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint64_t offset = 0;
+ uint16_t flags = 0;
+ std::string name;
+ std::shared_ptr<Elf> elf;
+ // The offset of the beginning of this mapping to the beginning of the
+ // ELF file.
+ // elf_offset == offset - elf_start_offset.
+ // This value is only non-zero if the offset is non-zero but there is
+ // no elf signature found at that offset.
+ uint64_t elf_offset = 0;
+ // This value is the offset into the file of the map in memory that is the
+ // start of the elf. This is not equal to offset when the linker splits
+ // shared libraries into a read-only and read-execute map.
+ uint64_t elf_start_offset = 0;
+
+ MapInfo* prev_map = nullptr;
+ // This is the previous map that is not empty with a 0 offset. For
+ // example, this set of maps:
+ // 1000-2000 r--p 000000 00:00 0 libc.so
+ // 2000-3000 ---p 000000 00:00 0 libc.so
+ // 3000-4000 r-xp 003000 00:00 0 libc.so
+ // The last map's prev_map would point to the 2000-3000 map, while the
+ // prev_real_map would point to the 1000-2000 map.
+ MapInfo* prev_real_map = nullptr;
+
+ std::atomic_int64_t load_bias;
+
+ // This is a pointer to a new'd std::string.
+ // Using an atomic value means that we don't need to lock and will
+ // make it easier to move to a fine grained lock in the future.
+ std::atomic_uintptr_t build_id;
+
+ // Set to true if the elf file data is coming from memory.
+ bool memory_backed_elf = false;
+
+ // This function guarantees it will never return nullptr.
+ Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
+
+ uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+
+ Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+ // Returns the raw build id read from the elf data.
+ std::string GetBuildID();
+
+ // Returns the printable version of the build id (hex dump of raw data).
+ std::string GetPrintableBuildID();
+
+ inline bool IsBlank() { return offset == 0 && flags == 0 && name.empty(); }
+
+ private:
+ MapInfo(const MapInfo&) = delete;
+ void operator=(const MapInfo&) = delete;
+
+ Memory* GetFileMemory();
+ bool InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory);
+
+ // Protect the creation of the elf object.
+ std::mutex mutex_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
new file mode 100644
index 000000000..e53f367d4
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/MapInfo.h>
+
+namespace unwindstack {
+
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int MAPS_FLAGS_JIT_SYMFILE_MAP = 0x4000;
+
+class Maps {
+ public:
+ virtual ~Maps() = default;
+
+ Maps() = default;
+
+ // Maps are not copyable but movable, because they own pointers to MapInfo
+ // objects.
+ Maps(const Maps&) = delete;
+ Maps& operator=(const Maps&) = delete;
+ Maps(Maps&&) = default;
+ Maps& operator=(Maps&&) = default;
+
+ MapInfo* Find(uint64_t pc);
+
+ virtual bool Parse();
+
+ virtual const std::string GetMapsFile() const { return ""; }
+
+ void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
+ uint64_t load_bias);
+
+ void Sort();
+
+ typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
+ iterator begin() { return maps_.begin(); }
+ iterator end() { return maps_.end(); }
+
+ typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
+ const_iterator begin() const { return maps_.begin(); }
+ const_iterator end() const { return maps_.end(); }
+
+ size_t Total() { return maps_.size(); }
+
+ MapInfo* Get(size_t index) {
+ if (index >= maps_.size()) return nullptr;
+ return maps_[index].get();
+ }
+
+ protected:
+ std::vector<std::unique_ptr<MapInfo>> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+ RemoteMaps(pid_t pid) : pid_(pid) {}
+ virtual ~RemoteMaps() = default;
+
+ virtual const std::string GetMapsFile() const override;
+
+ private:
+ pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+ LocalMaps() : RemoteMaps(getpid()) {}
+ virtual ~LocalMaps() = default;
+};
+
+class LocalUpdatableMaps : public Maps {
+ public:
+ LocalUpdatableMaps() : Maps() {}
+ virtual ~LocalUpdatableMaps() = default;
+
+ bool Reparse();
+
+ const std::string GetMapsFile() const override;
+
+ protected:
+ std::vector<std::unique_ptr<MapInfo>> saved_maps_;
+};
+
+class BufferMaps : public Maps {
+ public:
+ BufferMaps(const char* buffer) : buffer_(buffer) {}
+ virtual ~BufferMaps() = default;
+
+ bool Parse() override;
+
+ private:
+ const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+ FileMaps(const std::string& file) : file_(file) {}
+ virtual ~FileMaps() = default;
+
+ const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+ const std::string file_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
new file mode 100644
index 000000000..310656497
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_H
+#define _LIBUNWINDSTACK_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+namespace unwindstack {
+
+class Memory {
+ public:
+ Memory() = default;
+ virtual ~Memory() = default;
+
+ static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+ static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
+ static std::shared_ptr<Memory> CreateOfflineMemory(const uint8_t* data, uint64_t start,
+ uint64_t end);
+ static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
+
+ virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+
+ virtual void Clear() {}
+
+ virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+
+ bool ReadFully(uint64_t addr, void* dst, size_t size);
+
+ inline bool Read32(uint64_t addr, uint32_t* dst) {
+ return ReadFully(addr, dst, sizeof(uint32_t));
+ }
+
+ inline bool Read64(uint64_t addr, uint64_t* dst) {
+ return ReadFully(addr, dst, sizeof(uint64_t));
+ }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
new file mode 100644
index 000000000..4f761b4ef
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_H
+#define _LIBUNWINDSTACK_REGS_H
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+enum ArchEnum : uint8_t;
+class Memory;
+
+class Regs {
+ public:
+ enum LocationEnum : uint8_t {
+ LOCATION_UNKNOWN = 0,
+ LOCATION_REGISTER,
+ LOCATION_SP_OFFSET,
+ };
+
+ struct Location {
+ Location(LocationEnum type, int16_t value) : type(type), value(value) {}
+
+ LocationEnum type;
+ int16_t value;
+ };
+
+ Regs(uint16_t total_regs, const Location& return_loc)
+ : total_regs_(total_regs), return_loc_(return_loc) {}
+ virtual ~Regs() = default;
+
+ virtual ArchEnum Arch() = 0;
+
+ virtual bool Is32Bit() = 0;
+
+ virtual void* RawData() = 0;
+ virtual uint64_t pc() = 0;
+ virtual uint64_t sp() = 0;
+
+ virtual void set_pc(uint64_t pc) = 0;
+ virtual void set_sp(uint64_t sp) = 0;
+
+ uint64_t dex_pc() { return dex_pc_; }
+ void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
+
+ virtual uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) = 0;
+
+ virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
+
+ virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
+
+ virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) = 0;
+
+ uint16_t total_regs() { return total_regs_; }
+
+ virtual Regs* Clone() = 0;
+
+ static ArchEnum CurrentArch();
+ static Regs* RemoteGet(pid_t pid);
+ static Regs* CreateFromUcontext(ArchEnum arch, void* ucontext);
+ static Regs* CreateFromLocal();
+
+ protected:
+ uint16_t total_regs_;
+ Location return_loc_;
+ uint64_t dex_pc_ = 0;
+};
+
+template <typename AddressType>
+class RegsImpl : public Regs {
+ public:
+ RegsImpl(uint16_t total_regs, Location return_loc)
+ : Regs(total_regs, return_loc), regs_(total_regs) {}
+ virtual ~RegsImpl() = default;
+
+ bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
+
+ inline AddressType& operator[](size_t reg) { return regs_[reg]; }
+
+ void* RawData() override { return regs_.data(); }
+
+ virtual void IterateRegisters(std::function<void(const char*, uint64_t)> fn) override {
+ for (size_t i = 0; i < regs_.size(); ++i) {
+ fn(std::to_string(i).c_str(), regs_[i]);
+ }
+ }
+
+ protected:
+ std::vector<AddressType> regs_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
new file mode 100644
index 000000000..aa029be64
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_ARM_H
+#define _LIBUNWINDSTACK_REGS_ARM_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsArm : public RegsImpl<uint32_t> {
+ public:
+ RegsArm();
+ virtual ~RegsArm() = default;
+
+ ArchEnum Arch() override final;
+
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
+
+ Regs* Clone() override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_ARM_H
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
new file mode 100644
index 000000000..5cd7e5ba9
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_ARM64_H
+#define _LIBUNWINDSTACK_REGS_ARM64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsArm64 : public RegsImpl<uint64_t> {
+ public:
+ RegsArm64();
+ virtual ~RegsArm64() = default;
+
+ ArchEnum Arch() override final;
+
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
+
+ Regs* Clone() override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_ARM64_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
new file mode 100644
index 000000000..f0b5e3a1d
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+namespace unwindstack {
+
+#if defined(__arm__)
+
+inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
+ asm volatile(
+ ".align 2\n"
+ "bx pc\n"
+ "nop\n"
+ ".code 32\n"
+ "stmia %[base], {r0-r12}\n"
+ "add %[base], #52\n"
+ "mov r1, r13\n"
+ "mov r2, r14\n"
+ "mov r3, r15\n"
+ "stmia %[base], {r1-r3}\n"
+ "orr %[base], pc, #1\n"
+ "bx %[base]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "memory");
+}
+
+#elif defined(__aarch64__)
+
+inline __attribute__((__always_inline__)) void AsmGetRegs(void* reg_data) {
+ asm volatile(
+ "1:\n"
+ "stp x0, x1, [%[base], #0]\n"
+ "stp x2, x3, [%[base], #16]\n"
+ "stp x4, x5, [%[base], #32]\n"
+ "stp x6, x7, [%[base], #48]\n"
+ "stp x8, x9, [%[base], #64]\n"
+ "stp x10, x11, [%[base], #80]\n"
+ "stp x12, x13, [%[base], #96]\n"
+ "stp x14, x15, [%[base], #112]\n"
+ "stp x16, x17, [%[base], #128]\n"
+ "stp x18, x19, [%[base], #144]\n"
+ "stp x20, x21, [%[base], #160]\n"
+ "stp x22, x23, [%[base], #176]\n"
+ "stp x24, x25, [%[base], #192]\n"
+ "stp x26, x27, [%[base], #208]\n"
+ "stp x28, x29, [%[base], #224]\n"
+ "str x30, [%[base], #240]\n"
+ "mov x12, sp\n"
+ "adr x13, 1b\n"
+ "stp x12, x13, [%[base], #248]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "x12", "x13", "memory");
+}
+
+#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+#endif
+
+inline __attribute__((__always_inline__)) void RegsGetLocal(Regs* regs) {
+ AsmGetRegs(regs->RawData());
+}
+
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
new file mode 100644
index 000000000..8164a1515
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_MIPS_H
+#define _LIBUNWINDSTACK_REGS_MIPS_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips : public RegsImpl<uint32_t> {
+ public:
+ RegsMips();
+ virtual ~RegsMips() = default;
+
+ ArchEnum Arch() override final;
+
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
+
+ Regs* Clone() override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_MIPS_H
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
new file mode 100644
index 000000000..c98254259
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_MIPS64_H
+#define _LIBUNWINDSTACK_REGS_MIPS64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips64 : public RegsImpl<uint64_t> {
+ public:
+ RegsMips64();
+ virtual ~RegsMips64() = default;
+
+ ArchEnum Arch() override final;
+
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
+
+ Regs* Clone() override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
new file mode 100644
index 000000000..2323a4fc0
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_X86_H
+#define _LIBUNWINDSTACK_REGS_X86_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+struct x86_ucontext_t;
+
+class RegsX86 : public RegsImpl<uint32_t> {
+ public:
+ RegsX86();
+ virtual ~RegsX86() = default;
+
+ ArchEnum Arch() override final;
+
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
+
+ void SetFromUcontext(x86_ucontext_t* ucontext);
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
+
+ Regs* Clone() override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_X86_H
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
new file mode 100644
index 000000000..3e919a4f8
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_X86_64_H
+#define _LIBUNWINDSTACK_REGS_X86_64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+struct x86_64_ucontext_t;
+
+class RegsX86_64 : public RegsImpl<uint64_t> {
+ public:
+ RegsX86_64();
+ virtual ~RegsX86_64() = default;
+
+ ArchEnum Arch() override final;
+
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) override;
+
+ void SetFromUcontext(x86_64_ucontext_t* ucontext);
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
+
+ Regs* Clone() override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_X86_64_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm.h b/libunwindstack/include/unwindstack/UcontextArm.h
new file mode 100644
index 000000000..7d1ec3b9a
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextArm.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM_H
+#define _LIBUNWINDSTACK_UCONTEXT_ARM_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineArm.h>
+
+namespace unwindstack {
+
+struct arm_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct arm_mcontext_t {
+ uint32_t trap_no; // unsigned long
+ uint32_t error_code; // unsigned long
+ uint32_t oldmask; // unsigned long
+ uint32_t regs[ARM_REG_LAST]; // unsigned long
+ uint32_t cpsr; // unsigned long
+ uint32_t fault_address; // unsigned long
+};
+
+struct arm_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ arm_stack_t uc_stack;
+ arm_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_ARM_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm64.h b/libunwindstack/include/unwindstack/UcontextArm64.h
new file mode 100644
index 000000000..a68be3b64
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextArm64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+#define _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineArm64.h>
+
+namespace unwindstack {
+
+struct arm64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct arm64_sigset_t {
+ uint64_t sig; // unsigned long
+};
+
+struct arm64_mcontext_t {
+ uint64_t fault_address; // __u64
+ uint64_t regs[ARM64_REG_LAST]; // __u64
+ uint64_t pstate; // __u64
+ // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ arm64_stack_t uc_stack;
+ arm64_sigset_t uc_sigmask;
+ // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+ char __padding[128 - sizeof(arm64_sigset_t)];
+ // The full structure requires 16 byte alignment, but our partial structure
+ // doesn't, so force the alignment.
+ arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips.h b/libunwindstack/include/unwindstack/UcontextMips.h
new file mode 100644
index 000000000..02e33b64e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextMips.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineMips.h>
+
+namespace unwindstack {
+
+struct mips_stack_t {
+ uint32_t ss_sp; // void __user*
+ uint32_t ss_size; // size_t
+ int32_t ss_flags; // int
+};
+
+struct mips_mcontext_t {
+ uint32_t sc_regmask;
+ uint32_t sc_status;
+ uint64_t sc_pc;
+ uint64_t sc_regs[32];
+ // Nothing else is used, so don't define it.
+};
+
+struct mips_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ mips_stack_t uc_stack;
+ mips_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips64.h b/libunwindstack/include/unwindstack/UcontextMips64.h
new file mode 100644
index 000000000..5b92a55ae
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextMips64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineMips64.h>
+
+namespace unwindstack {
+
+struct mips64_stack_t {
+ uint64_t ss_sp; // void __user*
+ uint64_t ss_size; // size_t
+ int32_t ss_flags; // int
+};
+
+struct mips64_mcontext_t {
+ uint64_t sc_regs[32];
+ uint64_t sc_fpregs[32];
+ uint64_t sc_mdhi;
+ uint64_t sc_hi1;
+ uint64_t sc_hi2;
+ uint64_t sc_hi3;
+ uint64_t sc_mdlo;
+ uint64_t sc_lo1;
+ uint64_t sc_lo2;
+ uint64_t sc_lo3;
+ uint64_t sc_pc;
+ // Nothing else is used, so don't define it.
+};
+
+struct mips64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ mips64_stack_t uc_stack;
+ mips64_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86.h b/libunwindstack/include/unwindstack/UcontextX86.h
new file mode 100644
index 000000000..c96ebb70b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextX86.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_H
+#define _LIBUNWINDSTACK_UCONTEXT_X86_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineX86.h>
+
+namespace unwindstack {
+
+struct x86_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct x86_mcontext_t {
+ uint32_t gs;
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t efl;
+ uint32_t uesp;
+ uint32_t ss;
+ // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ x86_stack_t uc_stack;
+ x86_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_X86_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86_64.h b/libunwindstack/include/unwindstack/UcontextX86_64.h
new file mode 100644
index 000000000..4e163e5d4
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextX86_64.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+#define _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineX86_64.h>
+
+namespace unwindstack {
+
+struct x86_64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ int32_t pad;
+ uint64_t ss_size; // size_t
+};
+
+struct x86_64_mcontext_t {
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rsp;
+ uint64_t rip;
+ uint64_t efl;
+ uint64_t csgsfs;
+ uint64_t err;
+ uint64_t trapno;
+ uint64_t oldmask;
+ uint64_t cr2;
+ // Only care about the registers, skip everything else.
+};
+
+struct x86_64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ x86_64_stack_t uc_stack;
+ x86_64_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_X86_64_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
new file mode 100644
index 000000000..67762c0bd
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_UNWINDER_H
+#define _LIBUNWINDSTACK_UNWINDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+enum ArchEnum : uint8_t;
+
+struct FrameData {
+ size_t num;
+
+ uint64_t rel_pc;
+ uint64_t pc;
+ uint64_t sp;
+
+ std::string function_name;
+ uint64_t function_offset = 0;
+
+ std::string map_name;
+ // The offset from the first map representing the frame. When there are
+ // two maps (read-only and read-execute) this will be the offset from
+ // the read-only map. When there is only one map, this will be the
+ // same as the actual offset of the map and match map_exact_offset.
+ uint64_t map_elf_start_offset = 0;
+ // The actual offset from the map where the pc lies.
+ uint64_t map_exact_offset = 0;
+ uint64_t map_start = 0;
+ uint64_t map_end = 0;
+ uint64_t map_load_bias = 0;
+ int map_flags = 0;
+};
+
+class Unwinder {
+ public:
+ Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
+ : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+ frames_.reserve(max_frames);
+ }
+ Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
+ : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) {
+ frames_.reserve(max_frames);
+ }
+
+ virtual ~Unwinder() = default;
+
+ void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+ const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
+
+ size_t NumFrames() const { return frames_.size(); }
+
+ const std::vector<FrameData>& frames() { return frames_; }
+
+ std::vector<FrameData> ConsumeFrames() {
+ std::vector<FrameData> frames = std::move(frames_);
+ frames_.clear();
+ return frames;
+ }
+
+ std::string FormatFrame(size_t frame_num) const;
+ std::string FormatFrame(const FrameData& frame) const;
+
+ void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+
+ void SetRegs(Regs* regs) { regs_ = regs; }
+ Maps* GetMaps() { return maps_; }
+ std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
+
+ // Disabling the resolving of names results in the function name being
+ // set to an empty string and the function offset being set to zero.
+ void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
+ // Enable/disable soname printing the soname for a map name if the elf is
+ // embedded in a file. This is enabled by default.
+ // NOTE: This does nothing unless resolving names is enabled.
+ void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
+
+ void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
+
+ void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
+
+ bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
+
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ protected:
+ Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+
+ void FillInDexFrame();
+ FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
+
+ size_t max_frames_;
+ Maps* maps_;
+ Regs* regs_;
+ std::vector<FrameData> frames_;
+ std::shared_ptr<Memory> process_memory_;
+ JitDebug* jit_debug_ = nullptr;
+ DexFiles* dex_files_ = nullptr;
+ bool resolve_names_ = true;
+ bool embedded_soname_ = true;
+ bool display_build_id_ = false;
+ // True if at least one elf file is coming from memory and not the related
+ // file. This is only true if there is an actual file backing up the elf.
+ bool elf_from_memory_not_file_ = false;
+ ErrorData last_error_;
+};
+
+class UnwinderFromPid : public Unwinder {
+ public:
+ UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+ virtual ~UnwinderFromPid() = default;
+
+ bool Init(ArchEnum arch);
+
+ private:
+ pid_t pid_;
+ std::unique_ptr<Maps> maps_ptr_;
+ std::unique_ptr<JitDebug> jit_debug_ptr_;
+ std::unique_ptr<DexFiles> dex_files_ptr_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/UserArm.h b/libunwindstack/include/unwindstack/UserArm.h
new file mode 100644
index 000000000..7388c03e4
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserArm.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_ARM_H
+#define _LIBUNWINDSTACK_USER_ARM_H
+
+namespace unwindstack {
+
+struct arm_user_regs {
+ uint32_t regs[18];
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_ARM_H
diff --git a/libunwindstack/include/unwindstack/UserArm64.h b/libunwindstack/include/unwindstack/UserArm64.h
new file mode 100644
index 000000000..d74983fec
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserArm64.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_ARM64_H
+#define _LIBUNWINDSTACK_USER_ARM64_H
+
+namespace unwindstack {
+
+struct arm64_user_regs {
+ uint64_t regs[31];
+ uint64_t sp;
+ uint64_t pc;
+ uint64_t pstate;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UserMips.h b/libunwindstack/include/unwindstack/UserMips.h
new file mode 100644
index 000000000..184be4f39
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserMips.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS_H
+#define _LIBUNWINDSTACK_USER_MIPS_H
+
+namespace unwindstack {
+
+enum Mips32UserReg : uint16_t {
+ MIPS32_EF_R0 = 6,
+ MIPS32_EF_CP0_EPC = 40,
+};
+
+struct mips_user_regs {
+ uint32_t regs[45];
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UserMips64.h b/libunwindstack/include/unwindstack/UserMips64.h
new file mode 100644
index 000000000..c46befd4f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserMips64.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS64_H
+#define _LIBUNWINDSTACK_USER_MIPS64_H
+
+namespace unwindstack {
+
+enum Mips64UserReg : uint16_t {
+ MIPS64_EF_R0 = 0,
+ MIPS64_EF_CP0_EPC = 34,
+};
+
+struct mips64_user_regs {
+ uint64_t regs[45];
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UserX86.h b/libunwindstack/include/unwindstack/UserX86.h
new file mode 100644
index 000000000..a040560c7
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserX86.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_X86_H
+#define _LIBUNWINDSTACK_USER_X86_H
+
+namespace unwindstack {
+
+struct x86_user_regs {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t eax;
+ uint32_t xds;
+ uint32_t xes;
+ uint32_t xfs;
+ uint32_t xgs;
+ uint32_t orig_eax;
+ uint32_t eip;
+ uint32_t xcs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t xss;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_X86_H
diff --git a/libunwindstack/include/unwindstack/UserX86_64.h b/libunwindstack/include/unwindstack/UserX86_64.h
new file mode 100644
index 000000000..b80d201d5
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserX86_64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_X86_64_H
+#define _LIBUNWINDSTACK_USER_X86_64_H
+
+namespace unwindstack {
+
+struct x86_64_user_regs {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t orig_rax;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t eflags;
+ uint64_t rsp;
+ uint64_t ss;
+ uint64_t fs_base;
+ uint64_t gs_base;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_X86_64_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
new file mode 100644
index 000000000..69a781641
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -0,0 +1,1668 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <ios>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Log.h>
+#include <unwindstack/RegsArm.h>
+
+#include "ArmExidx.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
+ protected:
+ void Init(Memory* process_memory = nullptr) {
+ if (process_memory == nullptr) {
+ process_memory = &process_memory_;
+ }
+
+ regs_arm_.reset(new RegsArm());
+ for (size_t i = 0; i < regs_arm_->total_regs(); i++) {
+ (*regs_arm_)[i] = 0;
+ }
+ regs_arm_->set_pc(0);
+ regs_arm_->set_sp(0);
+
+ exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory));
+ if (log_ != ARM_LOG_NONE) {
+ exidx_->set_log(log_);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+ }
+ data_ = exidx_->data();
+ exidx_->set_cfa(0x10000);
+ }
+
+ void SetUp() override {
+ if (GetParam() == "no_logging") {
+ log_ = ARM_LOG_NONE;
+ } else if (GetParam() == "register_logging") {
+ log_ = ARM_LOG_BY_REG;
+ } else {
+ log_ = ARM_LOG_FULL;
+ }
+ elf_memory_.Clear();
+ process_memory_.Clear();
+ ResetExidx();
+ }
+
+ void ResetExidx() {
+ ResetLogs();
+ Init();
+ }
+
+ std::unique_ptr<ArmExidx> exidx_;
+ std::unique_ptr<RegsArm> regs_arm_;
+ std::deque<uint8_t>* data_;
+
+ MemoryFake elf_memory_;
+ MemoryFake process_memory_;
+ ArmLogType log_;
+};
+
+TEST_P(ArmExidxDecodeTest, vsp_incr) {
+ // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 4\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+
+ ResetExidx();
+ data_->clear();
+ data_->push_back(0x01);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 8\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->clear();
+ data_->push_back(0x3f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 256\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10100U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_decr) {
+ // 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4
+ data_->push_back(0x40);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 4\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0xfffcU, exidx_->cfa());
+
+ ResetExidx();
+ data_->clear();
+ data_->push_back(0x41);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 8\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0xfff8U, exidx_->cfa());
+
+ ResetExidx();
+ data_->clear();
+ data_->push_back(0x7f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 - 256\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0xff00U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, refuse_unwind) {
+ // 10000000 00000000: Refuse to unwind
+ data_->push_back(0x80);
+ data_->push_back(0x00);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_up_to_12) {
+ // 1000iiii iiiiiiii: Pop up to 12 integer registers
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ process_memory_.SetData32(0x10000, 0x10);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r15 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+
+ ResetExidx();
+ data_->push_back(0x8f);
+ data_->push_back(0xff);
+ for (size_t i = 0; i < 12; i++) {
+ process_memory_.SetData32(0x10000 + i * 4, i + 0x20);
+ }
+ exidx_->set_pc_set(false);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 48\n"
+ "4 unwind r4 = [cfa - 48]\n"
+ "4 unwind r5 = [cfa - 44]\n"
+ "4 unwind r6 = [cfa - 40]\n"
+ "4 unwind r7 = [cfa - 36]\n"
+ "4 unwind r8 = [cfa - 32]\n"
+ "4 unwind r9 = [cfa - 28]\n"
+ "4 unwind r10 = [cfa - 24]\n"
+ "4 unwind r11 = [cfa - 20]\n"
+ "4 unwind r12 = [cfa - 16]\n"
+ "4 unwind r13 = [cfa - 12]\n"
+ "4 unwind r14 = [cfa - 8]\n"
+ "4 unwind r15 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ // Popping r13 results in a modified cfa.
+ ASSERT_EQ(0x29U, exidx_->cfa());
+
+ ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x21U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x22U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x23U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x24U, (*exidx_->regs())[8]);
+ ASSERT_EQ(0x25U, (*exidx_->regs())[9]);
+ ASSERT_EQ(0x26U, (*exidx_->regs())[10]);
+ ASSERT_EQ(0x27U, (*exidx_->regs())[11]);
+ ASSERT_EQ(0x28U, (*exidx_->regs())[12]);
+ ASSERT_EQ(0x29U, (*exidx_->regs())[13]);
+ ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
+ ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
+
+ ResetExidx();
+ exidx_->set_cfa(0x10034);
+ data_->push_back(0x81);
+ data_->push_back(0x28);
+ process_memory_.SetData32(0x10034, 0x11);
+ process_memory_.SetData32(0x10038, 0x22);
+ process_memory_.SetData32(0x1003c, 0x33);
+ exidx_->set_pc_set(false);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 12\n"
+ "4 unwind r7 = [cfa - 12]\n"
+ "4 unwind r9 = [cfa - 8]\n"
+ "4 unwind r12 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10040U, exidx_->cfa());
+ ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x22U, (*exidx_->regs())[9]);
+ ASSERT_EQ(0x33U, (*exidx_->regs())[12]);
+}
+
+TEST_P(ArmExidxDecodeTest, set_vsp_from_register) {
+ // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
+
+ data_->push_back(0x90);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r0\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(1U, exidx_->cfa());
+
+ ResetExidx();
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
+ data_->push_back(0x93);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r3\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(4U, exidx_->cfa());
+
+ ResetExidx();
+ exidx_->set_cfa(0x100);
+ for (size_t i = 0; i < 15; i++) {
+ (*regs_arm_)[i] = i + 1;
+ }
+ data_->push_back(0x9e);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r14\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(15U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, reserved_prefix) {
+ // 10011101: Reserved as prefix for ARM register to register moves
+ data_->push_back(0x9d);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+
+ // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
+ ResetExidx();
+ data_->push_back(0x9f);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers) {
+ // 10100nnn: Pop r4-r[4+nnn]
+ data_->push_back(0xa0);
+ process_memory_.SetData32(0x10000, 0x14);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r4 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+ ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
+
+ ResetExidx();
+ data_->push_back(0xa3);
+ process_memory_.SetData32(0x10000, 0x20);
+ process_memory_.SetData32(0x10004, 0x30);
+ process_memory_.SetData32(0x10008, 0x40);
+ process_memory_.SetData32(0x1000c, 0x50);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r4 = [cfa - 16]\n"
+ "4 unwind r5 = [cfa - 12]\n"
+ "4 unwind r6 = [cfa - 8]\n"
+ "4 unwind r7 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+ ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
+
+ ResetExidx();
+ data_->push_back(0xa7);
+ process_memory_.SetData32(0x10000, 0x41);
+ process_memory_.SetData32(0x10004, 0x51);
+ process_memory_.SetData32(0x10008, 0x61);
+ process_memory_.SetData32(0x1000c, 0x71);
+ process_memory_.SetData32(0x10010, 0x81);
+ process_memory_.SetData32(0x10014, 0x91);
+ process_memory_.SetData32(0x10018, 0xa1);
+ process_memory_.SetData32(0x1001c, 0xb1);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 32\n"
+ "4 unwind r4 = [cfa - 32]\n"
+ "4 unwind r5 = [cfa - 28]\n"
+ "4 unwind r6 = [cfa - 24]\n"
+ "4 unwind r7 = [cfa - 20]\n"
+ "4 unwind r8 = [cfa - 16]\n"
+ "4 unwind r9 = [cfa - 12]\n"
+ "4 unwind r10 = [cfa - 8]\n"
+ "4 unwind r11 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10020U, exidx_->cfa());
+ ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x71U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x81U, (*exidx_->regs())[8]);
+ ASSERT_EQ(0x91U, (*exidx_->regs())[9]);
+ ASSERT_EQ(0xa1U, (*exidx_->regs())[10]);
+ ASSERT_EQ(0xb1U, (*exidx_->regs())[11]);
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) {
+ // 10101nnn: Pop r4-r[4+nnn], r14
+ data_->push_back(0xa8);
+ process_memory_.SetData32(0x10000, 0x12);
+ process_memory_.SetData32(0x10004, 0x22);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 8\n"
+ "4 unwind r4 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+ ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
+
+ ResetExidx();
+ data_->push_back(0xab);
+ process_memory_.SetData32(0x10000, 0x1);
+ process_memory_.SetData32(0x10004, 0x2);
+ process_memory_.SetData32(0x10008, 0x3);
+ process_memory_.SetData32(0x1000c, 0x4);
+ process_memory_.SetData32(0x10010, 0x5);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 20\n"
+ "4 unwind r4 = [cfa - 20]\n"
+ "4 unwind r5 = [cfa - 16]\n"
+ "4 unwind r6 = [cfa - 12]\n"
+ "4 unwind r7 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10014U, exidx_->cfa());
+ ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
+
+ ResetExidx();
+ data_->push_back(0xaf);
+ process_memory_.SetData32(0x10000, 0x1a);
+ process_memory_.SetData32(0x10004, 0x2a);
+ process_memory_.SetData32(0x10008, 0x3a);
+ process_memory_.SetData32(0x1000c, 0x4a);
+ process_memory_.SetData32(0x10010, 0x5a);
+ process_memory_.SetData32(0x10014, 0x6a);
+ process_memory_.SetData32(0x10018, 0x7a);
+ process_memory_.SetData32(0x1001c, 0x8a);
+ process_memory_.SetData32(0x10020, 0x9a);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 36\n"
+ "4 unwind r4 = [cfa - 36]\n"
+ "4 unwind r5 = [cfa - 32]\n"
+ "4 unwind r6 = [cfa - 28]\n"
+ "4 unwind r7 = [cfa - 24]\n"
+ "4 unwind r8 = [cfa - 20]\n"
+ "4 unwind r9 = [cfa - 16]\n"
+ "4 unwind r10 = [cfa - 12]\n"
+ "4 unwind r11 = [cfa - 8]\n"
+ "4 unwind r14 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10024U, exidx_->cfa());
+ ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
+ ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
+ ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
+ ASSERT_EQ(0x4aU, (*exidx_->regs())[7]);
+ ASSERT_EQ(0x5aU, (*exidx_->regs())[8]);
+ ASSERT_EQ(0x6aU, (*exidx_->regs())[9]);
+ ASSERT_EQ(0x7aU, (*exidx_->regs())[10]);
+ ASSERT_EQ(0x8aU, (*exidx_->regs())[11]);
+ ASSERT_EQ(0x9aU, (*exidx_->regs())[14]);
+}
+
+TEST_P(ArmExidxDecodeTest, finish) {
+ // 10110000: Finish
+ data_->push_back(0xb0);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa());
+ ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, spare) {
+ // 10110001 00000000: Spare
+ data_->push_back(0xb1);
+ data_->push_back(0x00);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa());
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+ // 10110001 xxxxyyyy: Spare (xxxx != 0000)
+ for (size_t x = 1; x < 16; x++) {
+ for (size_t y = 0; y < 16; y++) {
+ ResetExidx();
+ data_->push_back(0xb1);
+ data_->push_back((x << 4) | y);
+ ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+ }
+
+ // 101101nn: Spare
+ for (size_t n = 0; n < 4; n++) {
+ ResetExidx();
+ data_->push_back(0xb4 | n);
+ ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
+ ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+
+ // 11000111 00000000: Spare
+ ResetExidx();
+ data_->push_back(0xc7);
+ data_->push_back(0x00);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa());
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+
+ // 11000111 xxxxyyyy: Spare (xxxx != 0000)
+ for (size_t x = 1; x < 16; x++) {
+ for (size_t y = 0; y < 16; y++) {
+ ResetExidx();
+ data_->push_back(0xc7);
+ data_->push_back(0x10);
+ ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+ }
+
+ // 11001yyy: Spare (yyy != 000, 001)
+ for (size_t y = 2; y < 8; y++) {
+ ResetExidx();
+ data_->push_back(0xc8 | y);
+ ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+
+ // 11xxxyyy: Spare (xxx != 000, 001, 010)
+ for (size_t x = 3; x < 8; x++) {
+ for (size_t y = 0; y < 8; y++) {
+ ResetExidx();
+ data_->push_back(0xc0 | (x << 3) | y);
+ ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+ break;
+ }
+ ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
+ ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
+ }
+ }
+}
+
+TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) {
+ // 10110001 0000iiii: Pop integer registers {r0, r1, r2, r3}
+ data_->push_back(0xb1);
+ data_->push_back(0x01);
+ process_memory_.SetData32(0x10000, 0x45);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 4\n"
+ "4 unwind r0 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+ ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
+
+ ResetExidx();
+ data_->push_back(0xb1);
+ data_->push_back(0x0a);
+ process_memory_.SetData32(0x10000, 0x23);
+ process_memory_.SetData32(0x10004, 0x24);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 8\n"
+ "4 unwind r1 = [cfa - 8]\n"
+ "4 unwind r3 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+ ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
+ ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
+
+ ResetExidx();
+ data_->push_back(0xb1);
+ data_->push_back(0x0f);
+ process_memory_.SetData32(0x10000, 0x65);
+ process_memory_.SetData32(0x10004, 0x54);
+ process_memory_.SetData32(0x10008, 0x43);
+ process_memory_.SetData32(0x1000c, 0x32);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r0 = [cfa - 16]\n"
+ "4 unwind r1 = [cfa - 12]\n"
+ "4 unwind r2 = [cfa - 8]\n"
+ "4 unwind r3 = [cfa - 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+ ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
+ ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
+ ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
+ ASSERT_EQ(0x32U, (*exidx_->regs())[3]);
+}
+
+TEST_P(ArmExidxDecodeTest, vsp_large_incr) {
+ // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
+ data_->push_back(0xb2);
+ data_->push_back(0x7f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 1024\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10400U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xb2);
+ data_->push_back(0xff);
+ data_->push_back(0x02);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 2048\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10800U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xb2);
+ data_->push_back(0xff);
+ data_->push_back(0x82);
+ data_->push_back(0x30);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 3147776\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x310800U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
+ // 10110011 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
+ data_->push_back(0xb3);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xb3);
+ data_->push_back(0x48);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x1004cU, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
+ // 10111nnn: Pop VFP double precision registers D[8]-D[8+nnn] by FSTMFDX
+ data_->push_back(0xb8);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x1000cU, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xbb);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10024U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xbf);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10044U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
+ // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
+ data_->push_back(0xc0);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc2);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10018U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc5);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10030U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
+ // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
+ data_->push_back(0xc6);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc6);
+ data_->push_back(0x25);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10030U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc6);
+ data_->push_back(0xff);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10080U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
+ // 11000111 0000iiii: Intel Wireless MMX pop wCGR registes {wCGR0,1,2,3}
+ data_->push_back(0xc7);
+ data_->push_back(0x01);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10004U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc7);
+ data_->push_back(0x0a);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc7);
+ data_->push_back(0x0f);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
+ // 11001000 sssscccc: Pop VFP double precision registers d[16+ssss]-D[16+ssss+cccc] by VPUSH
+ data_->push_back(0xc8);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc8);
+ data_->push_back(0x14);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10028U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc8);
+ data_->push_back(0xff);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10080U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
+ // 11001001 sssscccc: Pop VFP double precision registers d[ssss]-D[ssss+cccc] by VPUSH
+ data_->push_back(0xc9);
+ data_->push_back(0x00);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc9);
+ data_->push_back(0x23);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10020U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xc9);
+ data_->push_back(0xff);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10080U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
+ // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
+ data_->push_back(0xd0);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10008U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xd2);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10018U, exidx_->cfa());
+
+ ResetExidx();
+ data_->push_back(0xd7);
+ ASSERT_TRUE(exidx_->Decode());
+ ASSERT_FALSE(exidx_->pc_set());
+ ASSERT_EQ("", GetFakeLogBuf());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10040U, exidx_->cfa());
+}
+
+TEST_P(ArmExidxDecodeTest, expect_truncated) {
+ // This test verifies that any op that requires extra ops will
+ // fail if the data is not present.
+ data_->push_back(0x80);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xb1);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xb2);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xb3);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc6);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc7);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc8);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+
+ data_->clear();
+ data_->push_back(0xc9);
+ ASSERT_FALSE(exidx_->Decode());
+ ASSERT_EQ(ARM_STATUS_TRUNCATED, exidx_->status());
+}
+
+TEST_P(ArmExidxDecodeTest, verify_no_truncated) {
+ // This test verifies that no pattern results in a crash or truncation.
+ MemoryFakeAlwaysReadZero memory_zero;
+ Init(&memory_zero);
+
+ for (size_t x = 0; x < 256; x++) {
+ if (x == 0xb2) {
+ // This opcode is followed by an uleb128, so just skip this one.
+ continue;
+ }
+ for (size_t y = 0; y < 256; y++) {
+ data_->clear();
+ data_->push_back(x);
+ data_->push_back(y);
+ if (!exidx_->Decode()) {
+ ASSERT_NE(ARM_STATUS_TRUNCATED, exidx_->status())
+ << "x y = 0x" << std::hex << x << " 0x" << y;
+ ASSERT_NE(ARM_STATUS_READ_FAILED, exidx_->status())
+ << "x y = 0x" << std::hex << x << " 0x" << y;
+ }
+ }
+ }
+}
+
+TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) {
+ // vsp = vsp + 4
+ data_->push_back(0x00);
+ // vsp = vsp + 12
+ data_->push_back(0x02);
+ // Finish
+ data_->push_back(0xb0);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind vsp = vsp + 4\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ("4 unwind cfa = r13 + 16\n", GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+ ASSERT_FALSE(exidx_->pc_set());
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 12
+ data_->push_back(0x02);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 16\n"
+ "4 unwind r15 = [cfa - 16]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10010U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_large_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 1024
+ data_->push_back(0xb2);
+ data_->push_back(0x7f);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 1024\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 1028\n"
+ "4 unwind r15 = [cfa - 1028]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10404U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_sub_after_pop) {
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp - 4
+ data_->push_back(0x41);
+ // Finish
+ data_->push_back(0xb0);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp - 8\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 - 4\n"
+ "4 unwind r15 = [cfa + 4]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0xfffcU, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_pc_set) {
+ // vsp = vsp + 4
+ data_->push_back(0x00);
+ // vsp = vsp + 12
+ data_->push_back(0x02);
+ // Pop {r15}
+ data_->push_back(0x88);
+ data_->push_back(0x00);
+ // vsp = vsp + 12
+ data_->push_back(0x02);
+ // Finish
+ data_->push_back(0xb0);
+
+ process_memory_.SetData32(0x10010, 0x10);
+
+ ASSERT_TRUE(exidx_->Eval());
+ switch (log_) {
+ case ARM_LOG_NONE:
+ ASSERT_EQ("", GetFakeLogPrint());
+ break;
+ case ARM_LOG_FULL:
+ ASSERT_EQ(
+ "4 unwind vsp = vsp + 4\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind pop {r15}\n"
+ "4 unwind vsp = vsp + 12\n"
+ "4 unwind finish\n",
+ GetFakeLogPrint());
+ break;
+ case ARM_LOG_BY_REG:
+ exidx_->LogByReg();
+ ASSERT_EQ(
+ "4 unwind cfa = r13 + 32\n"
+ "4 unwind r15 = [cfa - 16]\n",
+ GetFakeLogPrint());
+ break;
+ }
+ ASSERT_EQ(0x10020U, exidx_->cfa());
+ ASSERT_TRUE(exidx_->pc_set());
+ ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+INSTANTIATE_TEST_SUITE_P(Unwindstack, ArmExidxDecodeTest,
+ ::testing::Values("logging", "register_logging", "no_logging"));
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
new file mode 100644
index 000000000..79c799c3a
--- /dev/null
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class ArmExidxExtractTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ elf_memory_.Clear();
+ exidx_ = new ArmExidx(nullptr, &elf_memory_, nullptr);
+ data_ = exidx_->data();
+ data_->clear();
+ }
+
+ void TearDown() override {
+ delete exidx_;
+ }
+
+ ArmExidx* exidx_ = nullptr;
+ std::deque<uint8_t>* data_;
+ MemoryFake elf_memory_;
+};
+
+TEST_F(ArmExidxExtractTest, bad_alignment) {
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x1001));
+ ASSERT_EQ(ARM_STATUS_INVALID_ALIGNMENT, exidx_->status());
+ ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind) {
+ elf_memory_.SetData32(0x1000, 0x7fff2340);
+ elf_memory_.SetData32(0x1004, 1);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+ ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+ ASSERT_TRUE(data_->empty());
+}
+
+TEST_F(ArmExidxExtractTest, compact) {
+ elf_memory_.SetData32(0x4000, 0x7ffa3000);
+ elf_memory_.SetData32(0x4004, 0x80a8b0b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+ ASSERT_EQ(3U, data_->size());
+ ASSERT_EQ(0xa8, data_->at(0));
+ ASSERT_EQ(0xb0, data_->at(1));
+ ASSERT_EQ(0xb0, data_->at(2));
+
+ // Missing finish gets added.
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x534, 0x7ffa3000);
+ elf_memory_.SetData32(0x538, 0x80a1a2a3);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x534));
+ ASSERT_EQ(4U, data_->size());
+ ASSERT_EQ(0xa1, data_->at(0));
+ ASSERT_EQ(0xa2, data_->at(1));
+ ASSERT_EQ(0xa3, data_->at(2));
+ ASSERT_EQ(0xb0, data_->at(3));
+}
+
+TEST_F(ArmExidxExtractTest, compact_non_zero_personality) {
+ elf_memory_.SetData32(0x4000, 0x7ffa3000);
+
+ uint32_t compact_value = 0x80a8b0b0;
+ for (size_t i = 1; i < 16; i++) {
+ elf_memory_.SetData32(0x4004, compact_value | (i << 24));
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x4000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+ }
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) {
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x8100f3b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(2U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xb0, data_->at(1));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x8200f3f4);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(3U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xf4, data_->at(1));
+ ASSERT_EQ(0xb0, data_->at(2));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x8201f3f4);
+ elf_memory_.SetData32(0x6238, 0x102030b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(6U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xf4, data_->at(1));
+ ASSERT_EQ(0x10, data_->at(2));
+ ASSERT_EQ(0x20, data_->at(3));
+ ASSERT_EQ(0x30, data_->at(4));
+ ASSERT_EQ(0xb0, data_->at(5));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x8103f3f4);
+ elf_memory_.SetData32(0x6238, 0x10203040);
+ elf_memory_.SetData32(0x623c, 0x50607080);
+ elf_memory_.SetData32(0x6240, 0x90a0c0d0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(15U, data_->size());
+ ASSERT_EQ(0xf3, data_->at(0));
+ ASSERT_EQ(0xf4, data_->at(1));
+ ASSERT_EQ(0x10, data_->at(2));
+ ASSERT_EQ(0x20, data_->at(3));
+ ASSERT_EQ(0x30, data_->at(4));
+ ASSERT_EQ(0x40, data_->at(5));
+ ASSERT_EQ(0x50, data_->at(6));
+ ASSERT_EQ(0x60, data_->at(7));
+ ASSERT_EQ(0x70, data_->at(8));
+ ASSERT_EQ(0x80, data_->at(9));
+ ASSERT_EQ(0x90, data_->at(10));
+ ASSERT_EQ(0xa0, data_->at(11));
+ ASSERT_EQ(0xc0, data_->at(12));
+ ASSERT_EQ(0xd0, data_->at(13));
+ ASSERT_EQ(0xb0, data_->at(14));
+}
+
+TEST_F(ArmExidxExtractTest, second_read_compact_personality_illegal) {
+ elf_memory_.SetData32(0x5000, 0x7ffa1e48);
+ elf_memory_.SetData32(0x5004, 0x1230);
+ elf_memory_.SetData32(0x6234, 0x832132b0);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x7ffa1e48);
+ elf_memory_.SetData32(0x5004, 0x1230);
+ elf_memory_.SetData32(0x6234, 0x842132b0);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_offset_is_negative) {
+ elf_memory_.SetData32(0x5000, 0x7ffa1e48);
+ elf_memory_.SetData32(0x5004, 0x7fffb1e0);
+ elf_memory_.SetData32(0x1e4, 0x842132b0);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, second_read_not_compact) {
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x1);
+ elf_memory_.SetData32(0x6238, 0x001122b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(3U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0xb0, data_->at(2));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x2);
+ elf_memory_.SetData32(0x6238, 0x00112233);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(4U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0x33, data_->at(2));
+ ASSERT_EQ(0xb0, data_->at(3));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x3);
+ elf_memory_.SetData32(0x6238, 0x01112233);
+ elf_memory_.SetData32(0x623c, 0x445566b0);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(7U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0x33, data_->at(2));
+ ASSERT_EQ(0x44, data_->at(3));
+ ASSERT_EQ(0x55, data_->at(4));
+ ASSERT_EQ(0x66, data_->at(5));
+ ASSERT_EQ(0xb0, data_->at(6));
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x3);
+ elf_memory_.SetData32(0x6238, 0x05112233);
+ elf_memory_.SetData32(0x623c, 0x01020304);
+ elf_memory_.SetData32(0x6240, 0x05060708);
+ elf_memory_.SetData32(0x6244, 0x090a0b0c);
+ elf_memory_.SetData32(0x6248, 0x0d0e0f10);
+ elf_memory_.SetData32(0x624c, 0x11121314);
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(24U, data_->size());
+ ASSERT_EQ(0x11, data_->at(0));
+ ASSERT_EQ(0x22, data_->at(1));
+ ASSERT_EQ(0x33, data_->at(2));
+ ASSERT_EQ(0x01, data_->at(3));
+ ASSERT_EQ(0x02, data_->at(4));
+ ASSERT_EQ(0x03, data_->at(5));
+ ASSERT_EQ(0x04, data_->at(6));
+ ASSERT_EQ(0x05, data_->at(7));
+ ASSERT_EQ(0x06, data_->at(8));
+ ASSERT_EQ(0x07, data_->at(9));
+ ASSERT_EQ(0x08, data_->at(10));
+ ASSERT_EQ(0x09, data_->at(11));
+ ASSERT_EQ(0x0a, data_->at(12));
+ ASSERT_EQ(0x0b, data_->at(13));
+ ASSERT_EQ(0x0c, data_->at(14));
+ ASSERT_EQ(0x0d, data_->at(15));
+ ASSERT_EQ(0x0e, data_->at(16));
+ ASSERT_EQ(0x0f, data_->at(17));
+ ASSERT_EQ(0x10, data_->at(18));
+ ASSERT_EQ(0x11, data_->at(19));
+ ASSERT_EQ(0x12, data_->at(20));
+ ASSERT_EQ(0x13, data_->at(21));
+ ASSERT_EQ(0x14, data_->at(22));
+ ASSERT_EQ(0xb0, data_->at(23));
+}
+
+TEST_F(ArmExidxExtractTest, read_failures) {
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+ EXPECT_EQ(0x5004U, exidx_->status_address());
+
+ elf_memory_.SetData32(0x5000, 0x100);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+ EXPECT_EQ(0x5004U, exidx_->status_address());
+
+ elf_memory_.SetData32(0x5004, 0x100);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+ EXPECT_EQ(0x5104U, exidx_->status_address());
+
+ elf_memory_.SetData32(0x5104, 0x1);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+ EXPECT_EQ(0x5108U, exidx_->status_address());
+
+ elf_memory_.SetData32(0x5108, 0x01010203);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+ EXPECT_EQ(0x510cU, exidx_->status_address());
+}
+
+TEST_F(ArmExidxExtractTest, malformed) {
+ elf_memory_.SetData32(0x5000, 0x100);
+ elf_memory_.SetData32(0x5004, 0x100);
+ elf_memory_.SetData32(0x5104, 0x1);
+ elf_memory_.SetData32(0x5108, 0x06010203);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+
+ elf_memory_.Clear();
+ elf_memory_.SetData32(0x5000, 0x100);
+ elf_memory_.SetData32(0x5004, 0x100);
+ elf_memory_.SetData32(0x5104, 0x1);
+ elf_memory_.SetData32(0x5108, 0x81060203);
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status());
+}
+
+TEST_F(ArmExidxExtractTest, cant_unwind_log) {
+ elf_memory_.SetData32(0x1000, 0x7fff2340);
+ elf_memory_.SetData32(0x1004, 1);
+
+ exidx_->set_log(ARM_LOG_FULL);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+
+ ASSERT_FALSE(exidx_->ExtractEntryData(0x1000));
+ ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
+
+ ASSERT_EQ("4 unwind Raw Data: 0x00 0x00 0x00 0x01\n"
+ "4 unwind [cantunwind]\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_compact) {
+ elf_memory_.SetData32(0x4000, 0x7ffa3000);
+ elf_memory_.SetData32(0x4004, 0x80a8b0b0);
+
+ exidx_->set_log(ARM_LOG_FULL);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x4000));
+ ASSERT_EQ("4 unwind Raw Data: 0xa8 0xb0 0xb0\n", GetFakeLogPrint());
+}
+
+TEST_F(ArmExidxExtractTest, raw_data_non_compact) {
+ elf_memory_.SetData32(0x5000, 0x1234);
+ elf_memory_.SetData32(0x5004, 0x00001230);
+ elf_memory_.SetData32(0x6234, 0x2);
+ elf_memory_.SetData32(0x6238, 0x00112233);
+
+ exidx_->set_log(ARM_LOG_FULL);
+ exidx_->set_log_indent(0);
+ exidx_->set_log_skip_execution(false);
+
+ ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
+ ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h
new file mode 100644
index 000000000..6975c688b
--- /dev/null
+++ b/libunwindstack/tests/DexFileData.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
+#define _LIBUNWINDSTACK_DEXFILESDATA_H
+
+namespace unwindstack {
+
+// Borrowed from art/dex/dex_file_test.cc.
+static constexpr uint32_t kDexData[] = {
+ 0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+ 0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+ 0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+ 0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+ 0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+ 0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+ 0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+ 0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+ 0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+ 0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+ 0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+ 0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+ 0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+ 0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+ 0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+ 0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+ 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_DEXFILESDATA_H
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
new file mode 100644
index 000000000..dc935a36e
--- /dev/null
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <malloc.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFile.h"
+#include "DexFileData.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+TEST(DexFileTest, from_file_open_non_exist) {
+ EXPECT_TRUE(DexFileFromFile::Create(0, "/file/does/not/exist") == nullptr);
+}
+
+TEST(DexFileTest, from_file_open_too_small) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(size_t{10}, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, 10))));
+
+ // Header too small.
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
+
+ // Header correct, file too small.
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
+ ASSERT_EQ(sizeof(kDexData) - 1,
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 1))));
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
+}
+
+TEST(DexFileTest, from_file_open) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
+}
+
+TEST(DexFileTest, from_file_open_non_zero_offset) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET));
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ EXPECT_TRUE(DexFileFromFile::Create(0x100, tf.path) != nullptr);
+}
+
+static constexpr size_t kNumLeakLoops = 5000;
+static constexpr size_t kMaxAllowedLeakBytes = 1024;
+
+static void CheckForLeak(size_t loop, size_t* first_allocated_bytes, size_t* last_allocated_bytes) {
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (*first_allocated_bytes == 0) {
+ *first_allocated_bytes = allocated_bytes;
+ } else if (*last_allocated_bytes > *first_allocated_bytes) {
+ // Check that the total memory did not increase too much over the first loop.
+ ASSERT_LE(*last_allocated_bytes - *first_allocated_bytes, kMaxAllowedLeakBytes)
+ << "Failed in loop " << loop << " first_allocated_bytes " << *first_allocated_bytes
+ << " last_allocated_bytes " << *last_allocated_bytes;
+ }
+ *last_allocated_bytes = allocated_bytes;
+}
+
+TEST(DexFileTest, from_file_no_leak) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < kNumLeakLoops; i++) {
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
+ ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
+ }
+}
+
+TEST(DexFileTest, from_memory_fail_too_small_for_header) {
+ MemoryFake memory;
+
+ memory.SetMemory(0x1000, kDexData, 10);
+
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
+}
+
+TEST(DexFileTest, from_memory_fail_too_small_for_data) {
+ MemoryFake memory;
+
+ memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
+
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
+}
+
+TEST(DexFileTest, from_memory_open) {
+ MemoryFake memory;
+
+ memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
+
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
+}
+
+TEST(DexFileTest, from_memory_no_leak) {
+ MemoryFake memory;
+
+ memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
+
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < kNumLeakLoops; i++) {
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
+ ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
+ }
+}
+
+TEST(DexFileTest, create_using_file) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ MemoryFake memory;
+ MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_file_non_zero_start) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ MemoryFake memory;
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_file_non_zero_offset) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+ ASSERT_EQ(sizeof(kDexData),
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+ MemoryFake memory;
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_memory_empty_file) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+ EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_memory_file_does_not_exist) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
+}
+
+TEST(DexFileTest, create_using_memory_file_is_malformed) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_EQ(sizeof(kDexData) - 10,
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 10))));
+
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+ std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
+ ASSERT_TRUE(dex_file != nullptr);
+
+ // Check it came from memory by clearing memory and verifying it fails.
+ memory.Clear();
+ dex_file = DexFile::Create(0x4000, &memory, &info);
+ EXPECT_TRUE(dex_file == nullptr);
+}
+
+TEST(DexFileTest, get_method) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+ std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+ ASSERT_TRUE(dex_file != nullptr);
+
+ std::string method;
+ uint64_t method_offset;
+ ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset));
+ EXPECT_EQ("Main.<init>", method);
+ EXPECT_EQ(2U, method_offset);
+
+ ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
+ EXPECT_EQ("Main.main", method);
+ EXPECT_EQ(0U, method_offset);
+}
+
+TEST(DexFileTest, get_method_empty) {
+ MemoryFake memory;
+ memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+ MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
+ std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+ ASSERT_TRUE(dex_file != nullptr);
+
+ std::string method;
+ uint64_t method_offset;
+ EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
+
+ EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
new file mode 100644
index 000000000..477cf8efe
--- /dev/null
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFileData.h"
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DexFilesTest : public ::testing::Test {
+ protected:
+ void CreateFakeElf(MapInfo* map_info, uint64_t global_offset, uint64_t data_offset,
+ uint64_t data_vaddr, uint64_t data_size) {
+ MemoryFake* memory = new MemoryFake;
+ ElfFake* elf = new ElfFake(memory);
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+ elf->FakeSetInterface(interface);
+
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", global_offset);
+ interface->FakeSetDataOffset(data_offset);
+ interface->FakeSetDataVaddrStart(data_vaddr);
+ interface->FakeSetDataVaddrEnd(data_vaddr + data_size);
+ map_info->elf.reset(elf);
+ }
+
+ void Init(ArchEnum arch) {
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(arch);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
+ "4000-6000 r--s 00000000 00:00 0 /fake/elf\n"
+ "6000-8000 -wxs 00002000 00:00 0 /fake/elf\n"
+ "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n"
+ "c000-f000 rw-p 00002000 00:00 0 /fake/elf2\n"
+ "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+ "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
+ "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
+ "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
+ "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
+ "501000-502000 ---p 0000000 00:00 0\n"
+ "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
+ "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ // Global variable in a section that is not readable.
+ MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
+
+ // Global variable not set by default.
+ map_info = maps_->Get(kMapGlobalSetToZero);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
+
+ // Global variable set in this map.
+ map_info = maps_->Get(kMapGlobal);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
+
+ // Global variable set in this map, but there is an empty map before rw map.
+ map_info = maps_->Get(kMapGlobalAfterEmpty);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
+ }
+
+ void WriteDescriptor32(uint64_t addr, uint32_t head);
+ void WriteDescriptor64(uint64_t addr, uint64_t head);
+ void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
+ void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
+ void WriteDex(uint64_t dex_file);
+
+ static constexpr size_t kMapGlobalNonReadable = 2;
+ static constexpr size_t kMapGlobalSetToZero = 3;
+ static constexpr size_t kMapGlobal = 5;
+ static constexpr size_t kMapGlobalRw = 6;
+ static constexpr size_t kMapDexFileEntries = 7;
+ static constexpr size_t kMapDexFiles = 8;
+ static constexpr size_t kMapGlobalAfterEmpty = 9;
+ static constexpr size_t kMapDexFilesAfterEmpty = 12;
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+ std::unique_ptr<DexFiles> dex_files_;
+ std::unique_ptr<BufferMaps> maps_;
+};
+
+void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) {
+ // void* first_entry_
+ memory_->SetData32(addr + 12, head);
+}
+
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
+ // void* first_entry_
+ memory_->SetData64(addr + 16, head);
+}
+
+void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
+ uint32_t dex_file) {
+ // Format of the 32 bit DEXFileEntry structure:
+ // uint32_t next
+ memory_->SetData32(entry_addr, next);
+ // uint32_t prev
+ memory_->SetData32(entry_addr + 4, prev);
+ // uint32_t dex_file
+ memory_->SetData32(entry_addr + 8, dex_file);
+}
+
+void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
+ uint64_t dex_file) {
+ // Format of the 64 bit DEXFileEntry structure:
+ // uint64_t next
+ memory_->SetData64(entry_addr, next);
+ // uint64_t prev
+ memory_->SetData64(entry_addr + 8, prev);
+ // uint64_t dex_file
+ memory_->SetData64(entry_addr + 16, dex_file);
+}
+
+void DexFilesTest::WriteDex(uint64_t dex_file) {
+ memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
+}
+
+TEST_F(DexFilesTest, get_method_information_invalid) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFileEntries);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
+ EXPECT_EQ("nothing", method_name);
+ EXPECT_EQ(0x124U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_32) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_64) {
+ Init(ARCH_ARM64);
+
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor64(0x100800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x301000);
+ WriteDex(0x301000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(2U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+ WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+ Init(ARCH_ARM64);
+
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor64(0x100800, 0x200000);
+ WriteEntry64(0x200000, 0x200100, 0, 0x100000);
+ WriteEntry64(0x200100, 0, 0x200000, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(6U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_cached) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+
+ // Clear all memory and make sure that data is acquired from the cache.
+ memory_->Clear();
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_search_libs) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+ WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+ WriteDex(0x300000);
+
+ // Only search a given named list of libs.
+ std::vector<std::string> libs{"libart.so"};
+ dex_files_.reset(new DexFiles(process_memory_, libs));
+ dex_files_->SetArch(ARCH_ARM);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ EXPECT_EQ("nothing", method_name);
+ EXPECT_EQ(0x124U, method_offset);
+
+ MapInfo* map_info = maps_->Get(kMapGlobal);
+ map_info->name = "/system/lib/libart.so";
+ dex_files_.reset(new DexFiles(process_memory_, libs));
+ dex_files_->SetArch(ARCH_ARM);
+ // Set the rw map to the same name or this will not scan this entry.
+ map_info = maps_->Get(kMapGlobalRw);
+ map_info->name = "/system/lib/libart.so";
+ // Make sure that clearing out copy of the libs doesn't affect the
+ // DexFiles object.
+ libs.clear();
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ // First global variable found, but value is zero.
+ WriteDescriptor32(0xc800, 0);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+
+ // Verify that second is ignored when first is set to non-zero
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(ARCH_ARM);
+ method_name = "fail";
+ method_offset = 0x123;
+ WriteDescriptor32(0xc800, 0x100000);
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("fail", method_name);
+ EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+ Init(ARCH_ARM64);
+
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ // First global variable found, but value is zero.
+ WriteDescriptor64(0xc800, 0);
+
+ WriteDescriptor64(0x100800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+
+ // Verify that second is ignored when first is set to non-zero
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(ARCH_ARM64);
+ method_name = "fail";
+ method_offset = 0x123;
+ WriteDescriptor64(0xc800, 0x100000);
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("fail", method_name);
+ EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_with_empty_map) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
+
+ WriteDescriptor32(0x503800, 0x506000);
+ WriteEntry32(0x506000, 0, 0, 0x510000);
+ WriteDex(0x510000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
new file mode 100644
index 000000000..def408875
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+#include <type_traits>
+#include <unordered_map>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfCfa.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfCfaLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.pc_end = 0x2000;
+ fde_.pc_end = 0x10000;
+ fde_.cie = &cie_;
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCie cie_;
+ DwarfFde fde_;
+};
+TYPED_TEST_SUITE_P(DwarfCfaLogTest);
+
+// NOTE: All class variable references have to be prefaced with this->.
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ std::string expected = "4 unwind Illegal\n";
+ expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
+ std::string expected =
+ "4 unwind DW_CFA_offset register(3) 4\n"
+ "4 unwind Raw Data: 0x83 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_offset register(3) 132\n"
+ "4 unwind Raw Data: 0x83 0x84 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended register(3) 2\n"
+ "4 unwind Raw Data: 0x05 0x03 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(129) 2306\n"
+ "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
+ "4 unwind Raw Data: 0x11 0x05 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
+ "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3003));
+ expected =
+ "4 unwind DW_CFA_offset register(2) 4\n"
+ "4 unwind Raw Data: 0x82 0x04\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4000, 0x4002));
+ std::string expected =
+ "4 unwind DW_CFA_restore_extended register(8)\n"
+ "4 unwind Raw Data: 0x06 0x08\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5007));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(258) 4\n"
+ "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
+ "4 unwind DW_CFA_restore_extended register(258)\n"
+ "4 unwind Raw Data: 0x06 0x82 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (std::is_same<TypeParam, uint32_t>::value) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
+ std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ this->fde_.pc_start = address + 0x10;
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
+ expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x201));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2010\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x202));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2004\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x600, 0x603));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x505));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1022304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa02));
+ std::string expected =
+ "4 unwind DW_CFA_undefined register(9)\n"
+ "4 unwind Raw Data: 0x07 0x09\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1a00, 0x1a03));
+ expected =
+ "4 unwind DW_CFA_undefined register(129)\n"
+ "4 unwind Raw Data: 0x07 0x81 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
+ std::string expected =
+ "4 unwind DW_CFA_same_value register(127)\n"
+ "4 unwind Raw Data: 0x08 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_same_value register(255)\n"
+ "4 unwind Raw Data: 0x08 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x303));
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4305));
+ expected =
+ "4 unwind DW_CFA_register register(255) register(511)\n"
+ "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x301));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4301));
+
+ expected =
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3004));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n"
+ "4 unwind DW_CFA_def_cfa_offset 64\n"
+ "4 unwind Raw Data: 0x0e 0x40\n"
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa register(127) 116\n"
+ "4 unwind Raw Data: 0x0c 0x7f 0x74\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa register(383) 628\n"
+ "4 unwind Raw Data: 0x0c 0xff 0x02 0xf4 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
+ "4 unwind Raw Data: 0x12 0x30 0x25\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
+ "4 unwind Raw Data: 0x12 0xa3 0x01 0xfa 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_register register(114)\n"
+ "4 unwind Raw Data: 0x0d 0x72\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_register register(4217)\n"
+ "4 unwind Raw Data: 0x0d 0xf9 0x20\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 1364\n"
+ "4 unwind Raw Data: 0x0e 0xd4 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
+ "4 unwind Raw Data: 0x13 0xf6 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x106));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_expression 4\n"
+ "4 unwind Raw Data: 0x0f 0x04 0x01 0x02 0x04 0x05\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x01\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x02\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x04\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ expected = "4 unwind Raw Data: 0x0f 0x81 0x01";
+ std::string op_string;
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(0x05);
+ op_string +=
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ expected += " 0x05";
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected += '\n';
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x284));
+
+ expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_expression register(4) 2\n"
+ "4 unwind Raw Data: 0x10 0x04 0x02 0xc0 0xc1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ expected = "4 unwind Raw Data: 0x10 0xff 0x01 0x82 0x01";
+ std::string op_string;
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(0xa0 + (i - 5) % 96);
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
+
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x287));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset register(69) 84\n"
+ "4 unwind Raw Data: 0x14 0x45 0x54\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x400, 0x405));
+
+ expected =
+ "4 unwind DW_CFA_val_offset register(290) 692\n"
+ "4 unwind Raw Data: 0x14 0xa2 0x02 0xb4 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
+ "4 unwind Raw Data: 0x15 0x56 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa05));
+
+ expected =
+ "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
+ "4 unwind Raw Data: 0x15 0xff 0x01 0xc0 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_expression register(5) 2\n"
+ "4 unwind Raw Data: 0x16 0x05 0x02 0xb0 0xb1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ expected = "4 unwind Raw Data: 0x16 0x83 0x10 0xa8 0x01";
+ std::string op_string;
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(0xa0 + (i % 96));
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 6) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_val_expression register(2051) 168\n" + expected + "\n";
+
+ this->memory_.SetMemory(0xa00, ops);
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xaad));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_args_size 4\n"
+ "4 unwind Raw Data: 0x2e 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5004));
+
+ expected =
+ "4 unwind DW_CFA_GNU_args_size 65572\n"
+ "4 unwind Raw Data: 0x2e 0xa4 0x80 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
+ "4 unwind Raw Data: 0x2f 0x08 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
+
+ expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
+ "4 unwind Raw Data: 0x2f 0x81 0x02 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x306));
+
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n"
+ "4 unwind DW_CFA_register register(2) register(4)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+ cfa_undefined, cfa_same, cfa_register, cfa_state,
+ cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+ cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+ cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+ cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
new file mode 100644
index 000000000..9c6ab05e6
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -0,0 +1,968 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfCfa.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfCfaTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.cie = &cie_;
+
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCie cie_;
+ DwarfFde fde_;
+};
+TYPED_TEST_SUITE_P(DwarfCfaTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->LastErrorCode());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+// This test needs to be examined.
+TYPED_TEST_P(DwarfCfaTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(32U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(1056U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2306U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(0x80U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(134);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-8), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[2] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3003, &loc_regs));
+ ASSERT_EQ(0x3003U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
+ ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[258] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5007, &loc_regs));
+ ASSERT_EQ(0x5007U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(258);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (sizeof(TypeParam) == 4) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ loc_regs.clear();
+ this->fde_.pc_start = address + 0x10;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ std::string cur_address_str(address_str);
+ cur_address_str[cur_address_str.size() - 2] = '8';
+ std::string expected = "4 unwind Warning: PC is moving backwards: old " + cur_address_str +
+ " new " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x202, &loc_regs));
+ ASSERT_EQ(0x202U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x600, 0x603, &loc_regs));
+ ASSERT_EQ(0x603U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0xc10U, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x505, &loc_regs));
+ ASSERT_EQ(0x505U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x4080c10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa02, &loc_regs));
+ ASSERT_EQ(0xa02U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(9);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1a00, 0x1a03, &loc_regs));
+ ASSERT_EQ(0x1a03U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[127] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(127));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ loc_regs[255] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(255));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x303, &loc_regs));
+ ASSERT_EQ(0x303U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4305, &loc_regs));
+ ASSERT_EQ(0x4305U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(511U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x301, &loc_regs));
+ ASSERT_EQ(0x301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4301, &loc_regs));
+ ASSERT_EQ(0x4301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x85, 0x02, 0x0a, 0x86, 0x04, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2005, &loc_regs));
+ ASSERT_EQ(0x2005U, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2006, &loc_regs));
+ ASSERT_EQ(0x2006U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ ResetLogs();
+ this->memory_.SetMemory(
+ 0x6000, std::vector<uint8_t>{0x0a, 0x85, 0x02, 0x0a, 0x86, 0x04, 0x0a, 0x87, 0x01, 0x0a, 0x89,
+ 0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600c, &loc_regs));
+ ASSERT_EQ(0x600cU, this->dmem_->cur_offset());
+ ASSERT_EQ(4U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(9));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600d, &loc_regs));
+ ASSERT_EQ(0x600dU, this->dmem_->cur_offset());
+ ASSERT_EQ(3U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600e, &loc_regs));
+ ASSERT_EQ(0x600eU, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600f, &loc_regs));
+ ASSERT_EQ(0x600fU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6010, &loc_regs));
+ ASSERT_EQ(0x6010U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6011, &loc_regs));
+ ASSERT_EQ(0x6011U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+}
+
+// This test verifies that the cfa offset is saved and restored properly.
+// Even though the spec is not clear about whether the offset is also
+// restored, the gcc unwinder does, and libunwind does too.
+TYPED_TEST_P(DwarfCfaTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+ dwarf_loc_regs_t loc_regs;
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {5, 100}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3004, &loc_regs));
+ ASSERT_EQ(0x3004U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(5U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(100U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x7fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x74U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x17fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x274U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x30U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x128U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0xa3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<uint64_t>(-48), loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
+
+ ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 20}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x72U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(20U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 60}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x1079U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(60U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x59U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x554U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x118U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<TypeParam>(-80), static_cast<TypeParam>(loc_regs[CFA_REG].values[1]));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x03, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x106, &loc_regs));
+ ASSERT_EQ(0x106U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(i - 1);
+ }
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
+ ASSERT_EQ(0x284U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x81U, loc_regs[CFA_REG].values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0x40, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(4);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(i - 4);
+ }
+
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x287, &loc_regs));
+ ASSERT_EQ(0x287U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(130U, location->second.values[0]);
+ ASSERT_EQ(0x287U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(69);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x2a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x400, 0x405, &loc_regs));
+ ASSERT_EQ(0x405U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(290);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x15a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(86);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x90U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa05, &loc_regs));
+ ASSERT_EQ(0xa05U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-512), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0x10, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(i);
+ }
+
+ this->memory_.SetMemory(0xa00, ops);
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xaad, &loc_regs));
+ ASSERT_EQ(0xaadU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(2051);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(168U, location->second.values[0]);
+ ASSERT_EQ(0xaadU, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5004, &loc_regs));
+ ASSERT_EQ(0x5004U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(8);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-16), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(257);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-255), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x306, &loc_regs));
+ ASSERT_EQ(0x306U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(4U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+ cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+ cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+ cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+ cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
+ cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
+ cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaTest, DwarfCfaTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
new file mode 100644
index 000000000..fac8a0e3f
--- /dev/null
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfDebugFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ debug_frame_ = new DwarfDebugFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete debug_frame_; }
+
+ MemoryFake memory_;
+ DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+};
+TYPED_TEST_SUITE_P(DwarfDebugFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+static void SetCie32(MemoryFake* memory, uint64_t offset, uint32_t length,
+ std::vector<uint8_t> data) {
+ memory->SetData32(offset, length);
+ offset += 4;
+ // Indicates this is a cie.
+ memory->SetData32(offset, 0xffffffff);
+ offset += 4;
+ memory->SetMemory(offset, data);
+}
+
+static void SetCie64(MemoryFake* memory, uint64_t offset, uint64_t length,
+ std::vector<uint8_t> data) {
+ memory->SetData32(offset, 0xffffffff);
+ offset += 4;
+ memory->SetData64(offset, length);
+ offset += 8;
+ // Indicates this is a cie.
+ memory->SetData64(offset, 0xffffffffffffffffUL);
+ offset += 8;
+ memory->SetMemory(offset, data);
+}
+
+static void SetFde32(MemoryFake* memory, uint64_t offset, uint32_t length, uint64_t cie_offset,
+ uint32_t pc_start, uint32_t pc_length, uint64_t segment_length = 0,
+ std::vector<uint8_t>* data = nullptr) {
+ memory->SetData32(offset, length);
+ offset += 4;
+ memory->SetData32(offset, cie_offset);
+ offset += 4 + segment_length;
+ memory->SetData32(offset, pc_start);
+ offset += 4;
+ memory->SetData32(offset, pc_length);
+ if (data != nullptr) {
+ offset += 4;
+ memory->SetMemory(offset, *data);
+ }
+}
+
+static void SetFde64(MemoryFake* memory, uint64_t offset, uint64_t length, uint64_t cie_offset,
+ uint64_t pc_start, uint64_t pc_length, uint64_t segment_length = 0,
+ std::vector<uint8_t>* data = nullptr) {
+ memory->SetData32(offset, 0xffffffff);
+ offset += 4;
+ memory->SetData64(offset, length);
+ offset += 8;
+ memory->SetData64(offset, cie_offset);
+ offset += 8 + segment_length;
+ memory->SetData64(offset, pc_start);
+ offset += 8;
+ memory->SetData64(offset, pc_length);
+ if (data != nullptr) {
+ offset += 8;
+ memory->SetMemory(offset, *data);
+ }
+}
+
+static void SetFourFdes32(MemoryFake* memory) {
+ SetCie32(memory, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+ // FDE 32 information.
+ SetFde32(memory, 0x5100, 0xfc, 0, 0x1500, 0x200);
+ SetFde32(memory, 0x5200, 0xfc, 0, 0x2500, 0x300);
+
+ // CIE 32 information.
+ SetCie32(memory, 0x5300, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+ // FDE 32 information.
+ SetFde32(memory, 0x5400, 0xfc, 0x300, 0x3500, 0x400);
+ SetFde32(memory, 0x5500, 0xfc, 0x300, 0x4500, 0x500);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32) {
+ SetFourFdes32(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+
+ ASSERT_EQ(4U, fdes.size());
+
+ EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+ EXPECT_EQ(0x5110U, fdes[0]->cfa_instructions_offset);
+ EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+ EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+ EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+ EXPECT_EQ(0U, fdes[0]->lsda_address);
+ EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+ EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+ EXPECT_EQ(0x5210U, fdes[1]->cfa_instructions_offset);
+ EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+ EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+ EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+ EXPECT_EQ(0U, fdes[1]->lsda_address);
+ EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+ EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+ EXPECT_EQ(0x5410U, fdes[2]->cfa_instructions_offset);
+ EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+ EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+ EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+ EXPECT_EQ(0U, fdes[2]->lsda_address);
+ EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+ EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+ EXPECT_EQ(0x5510U, fdes[3]->cfa_instructions_offset);
+ EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+ EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+ EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+ EXPECT_EQ(0U, fdes[3]->lsda_address);
+ EXPECT_TRUE(fdes[3]->cie != nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_after_GetFdeFromPc) {
+ SetFourFdes32(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x3600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x3500U, fde->pc_start);
+ EXPECT_EQ(0x3900U, fde->pc_end);
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+ ASSERT_EQ(4U, fdes.size());
+
+ // Verify that they got added in the correct order.
+ EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+ EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+ EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+ EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+ EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+ EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+ EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+ EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_not_in_section) {
+ SetFourFdes32(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+
+ ASSERT_EQ(3U, fdes.size());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32) {
+ SetFourFdes32(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x1500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x2600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x3600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x3500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_reverse) {
+ SetFourFdes32(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x3600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x3500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x2600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x1600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x1500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_not_in_section) {
+ SetFourFdes32(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+static void SetFourFdes64(MemoryFake* memory) {
+ // CIE 64 information.
+ SetCie64(memory, 0x5000, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+ // FDE 64 information.
+ SetFde64(memory, 0x5100, 0xf4, 0, 0x1500, 0x200);
+ SetFde64(memory, 0x5200, 0xf4, 0, 0x2500, 0x300);
+
+ // CIE 64 information.
+ SetCie64(memory, 0x5300, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+ // FDE 64 information.
+ SetFde64(memory, 0x5400, 0xf4, 0x300, 0x3500, 0x400);
+ SetFde64(memory, 0x5500, 0xf4, 0x300, 0x4500, 0x500);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64) {
+ SetFourFdes64(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+
+ ASSERT_EQ(4U, fdes.size());
+
+ EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+ EXPECT_EQ(0x5124U, fdes[0]->cfa_instructions_offset);
+ EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+ EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+ EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+ EXPECT_EQ(0U, fdes[0]->lsda_address);
+ EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+ EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+ EXPECT_EQ(0x5224U, fdes[1]->cfa_instructions_offset);
+ EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+ EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+ EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+ EXPECT_EQ(0U, fdes[1]->lsda_address);
+ EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+ EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+ EXPECT_EQ(0x5424U, fdes[2]->cfa_instructions_offset);
+ EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+ EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+ EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+ EXPECT_EQ(0U, fdes[2]->lsda_address);
+ EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+ EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+ EXPECT_EQ(0x5524U, fdes[3]->cfa_instructions_offset);
+ EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+ EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+ EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+ EXPECT_EQ(0U, fdes[3]->lsda_address);
+ EXPECT_TRUE(fdes[3]->cie != nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_after_GetFdeFromPc) {
+ SetFourFdes64(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+ EXPECT_EQ(0x2800U, fde->pc_end);
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+ ASSERT_EQ(4U, fdes.size());
+
+ // Verify that they got added in the correct order.
+ EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+ EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+ EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+ EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+ EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+ EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+ EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+ EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_not_in_section) {
+ SetFourFdes64(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+
+ ASSERT_EQ(3U, fdes.size());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64) {
+ SetFourFdes64(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x1500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x2600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x3600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x3500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_reverse) {
+ SetFourFdes64(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x3600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x3500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x2600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x2500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x1600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x1500U, fde->pc_start);
+
+ fde = this->debug_frame_->GetFdeFromPc(0);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_not_in_section) {
+ SetFourFdes64(&this->memory_);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
+ SetCie32(&this->memory_, 0xf000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+ SetFde32(&this->memory_, 0x14000, 0x20, 0xf000, 0x9000, 0x100);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x9000U, fde->pc_start);
+ EXPECT_EQ(0x9100U, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
+ SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+ SetFde64(&this->memory_, 0x8000, 0x200, 0x6000, 0x5000, 0x300);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x5000U, fde->pc_start);
+ EXPECT_EQ(0x5300U, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+static void VerifyCieVersion(const DwarfCie* cie, uint8_t version, uint8_t segment_size,
+ uint8_t fde_encoding, uint64_t return_address, uint64_t start_offset,
+ uint64_t end_offset) {
+ EXPECT_EQ(version, cie->version);
+ EXPECT_EQ(fde_encoding, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(segment_size, cie->segment_size);
+ EXPECT_EQ(1U, cie->augmentation_string.size());
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(return_address, cie->return_address_register);
+ EXPECT_EQ(0x5000U + start_offset, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5000U + end_offset, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_cie_cached) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+
+ std::vector<uint8_t> zero(0x100, 0);
+ this->memory_.SetMemory(0x5000, zero);
+ cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_cie_cached) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+
+ std::vector<uint8_t> zero(0x100, 0);
+ this->memory_.SetMemory(0x5000, zero);
+ cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version1) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version1) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version3) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata4, 0x181, 0xe, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version3) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata8, 0x181, 0x1a, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+ ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+ SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+ ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+
+ SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
+ ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+ SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
+ ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+}
+
+static void VerifyCieAugment(const DwarfCie* cie, uint64_t inst_offset, uint64_t inst_end) {
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ(5U, cie->augmentation_string.size());
+ EXPECT_EQ('z', cie->augmentation_string[0]);
+ EXPECT_EQ('L', cie->augmentation_string[1]);
+ EXPECT_EQ('P', cie->augmentation_string[2]);
+ EXPECT_EQ('R', cie->augmentation_string[3]);
+ EXPECT_EQ('\0', cie->augmentation_string[4]);
+ EXPECT_EQ(0x12345678U, cie->personality_handler);
+ EXPECT_EQ(4U, cie->code_alignment_factor);
+ EXPECT_EQ(8, cie->data_alignment_factor);
+ EXPECT_EQ(0x10U, cie->return_address_register);
+ EXPECT_EQ(inst_offset, cie->cfa_instructions_offset);
+ EXPECT_EQ(inst_end, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_augment) {
+ SetCie32(&this->memory_, 0x5000, 0x100,
+ std::vector<uint8_t>{/* version */ 1,
+ /* augment string */ 'z', 'L', 'P', 'R', '\0',
+ /* code alignment factor */ 4,
+ /* data alignment factor */ 8,
+ /* return address register */ 0x10,
+ /* augment length */ 0xf,
+ /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+ /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+ /* R data */ DW_EH_PE_udata2});
+
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieAugment(cie, 0x5021, 0x5104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_augment) {
+ SetCie64(&this->memory_, 0x5000, 0x100,
+ std::vector<uint8_t>{/* version */ 1,
+ /* augment string */ 'z', 'L', 'P', 'R', '\0',
+ /* code alignment factor */ 4,
+ /* data alignment factor */ 8,
+ /* return address register */ 0x10,
+ /* augment length */ 0xf,
+ /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+ /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+ /* R data */ DW_EH_PE_udata2});
+
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieAugment(cie, 0x502d, 0x510c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_augment) {
+ SetCie32(&this->memory_, 0x5000, 0xfc,
+ std::vector<uint8_t>{/* version */ 4,
+ /* augment string */ 'z', '\0',
+ /* address size */ 8,
+ /* segment size */ 0x10,
+ /* code alignment factor */ 16,
+ /* data alignment factor */ 32,
+ /* return address register */ 10,
+ /* augment length */ 0x0});
+
+ std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+ SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(4U, fde->cie->version);
+ EXPECT_EQ(0x5000U, fde->cie_offset);
+ EXPECT_EQ(0x53a2U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x4300U, fde->pc_start);
+ EXPECT_EQ(0x4600U, fde->pc_end);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_augment) {
+ SetCie64(&this->memory_, 0x5000, 0xfc,
+ std::vector<uint8_t>{/* version */ 4,
+ /* augment string */ 'z', '\0',
+ /* address size */ 8,
+ /* segment size */ 0x10,
+ /* code alignment factor */ 16,
+ /* data alignment factor */ 32,
+ /* return address register */ 10,
+ /* augment length */ 0x0});
+
+ std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+ SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(4U, fde->cie->version);
+ EXPECT_EQ(0x5000U, fde->cie_offset);
+ EXPECT_EQ(0x53b6U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x4300U, fde->pc_start);
+ EXPECT_EQ(0x4600U, fde->pc_end);
+ EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_lsda_address) {
+ SetCie32(&this->memory_, 0x5000, 0xfc,
+ std::vector<uint8_t>{/* version */ 1,
+ /* augment string */ 'z', 'L', '\0',
+ /* address size */ 8,
+ /* code alignment factor */ 16,
+ /* data alignment factor */ 32,
+ /* return address register */ 10,
+ /* augment length */ 0x2,
+ /* L data */ DW_EH_PE_udata2});
+
+ std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+ /* lsda address */ 0x20, 0x45};
+ SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(0x5000U, fde->cie_offset);
+ EXPECT_EQ(0x5392U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x4300U, fde->pc_start);
+ EXPECT_EQ(0x4600U, fde->pc_end);
+ EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_lsda_address) {
+ SetCie64(&this->memory_, 0x5000, 0xfc,
+ std::vector<uint8_t>{/* version */ 1,
+ /* augment string */ 'z', 'L', '\0',
+ /* address size */ 8,
+ /* code alignment factor */ 16,
+ /* data alignment factor */ 32,
+ /* return address register */ 10,
+ /* augment length */ 0x2,
+ /* L data */ DW_EH_PE_udata2});
+
+ std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+ /* lsda address */ 0x20, 0x45};
+ SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+ ASSERT_TRUE(fde != nullptr);
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(0x5000U, fde->cie_offset);
+ EXPECT_EQ(0x53a6U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0x4300U, fde->pc_start);
+ EXPECT_EQ(0x4600U, fde->pc_end);
+ EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc_interleaved) {
+ SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+ // FDE 0 (0x100 - 0x200)
+ SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0x100, 0x100);
+ // FDE 1 (0x300 - 0x500)
+ SetFde32(&this->memory_, 0x5200, 0xfc, 0, 0x300, 0x200);
+ // FDE 2 (0x700 - 0x800)
+ SetFde32(&this->memory_, 0x5300, 0xfc, 0, 0x700, 0x100);
+ // FDE 3 (0xa00 - 0xb00)
+ SetFde32(&this->memory_, 0x5400, 0xfc, 0, 0xa00, 0x100);
+ // FDE 4 (0x100 - 0xb00)
+ SetFde32(&this->memory_, 0x5500, 0xfc, 0, 0x150, 0xa00);
+ // FDE 5 (0x50 - 0xa0)
+ SetFde32(&this->memory_, 0x5600, 0xfc, 0, 0x50, 0x50);
+ // FDE 6 (0x0 - 0x50)
+ SetFde32(&this->memory_, 0x5700, 0xfc, 0, 0, 0x50);
+
+ this->debug_frame_->Init(0x5000, 0x800, 0);
+
+ // Force reading all entries so no entries are found.
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
+ ASSERT_TRUE(fde == nullptr);
+
+ // 0x50 - 0xa0 FDE 5
+ fde = this->debug_frame_->GetFdeFromPc(0x60);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x50U, fde->pc_start);
+ EXPECT_EQ(0xa0U, fde->pc_end);
+
+ // 0x0 - 0x50 FDE 6
+ fde = this->debug_frame_->GetFdeFromPc(0x10);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0U, fde->pc_start);
+ EXPECT_EQ(0x50U, fde->pc_end);
+
+ // 0x100 - 0x200 FDE 0
+ fde = this->debug_frame_->GetFdeFromPc(0x170);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x100U, fde->pc_start);
+ EXPECT_EQ(0x200U, fde->pc_end);
+
+ // 0x200 - 0x300 FDE 4
+ fde = this->debug_frame_->GetFdeFromPc(0x210);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x150U, fde->pc_start);
+ EXPECT_EQ(0xb50U, fde->pc_end);
+
+ // 0x300 - 0x500 FDE 1
+ fde = this->debug_frame_->GetFdeFromPc(0x310);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x300U, fde->pc_start);
+ EXPECT_EQ(0x500U, fde->pc_end);
+
+ // 0x700 - 0x800 FDE 2
+ fde = this->debug_frame_->GetFdeFromPc(0x790);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x700U, fde->pc_start);
+ EXPECT_EQ(0x800U, fde->pc_end);
+
+ // 0x800 - 0x900 FDE 4
+ fde = this->debug_frame_->GetFdeFromPc(0x850);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x150U, fde->pc_start);
+ EXPECT_EQ(0xb50U, fde->pc_end);
+
+ // 0xa00 - 0xb00 FDE 3
+ fde = this->debug_frame_->GetFdeFromPc(0xa35);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0xa00U, fde->pc_start);
+ EXPECT_EQ(0xb00U, fde->pc_end);
+
+ // 0xb00 - 0xb50 FDE 4
+ fde = this->debug_frame_->GetFdeFromPc(0xb20);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x150U, fde->pc_start);
+ EXPECT_EQ(0xb50U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc_overlap) {
+ SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+ // FDE 0 (0x100 - 0x200)
+ SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0x100, 0x100);
+ // FDE 1 (0x50 - 0x550)
+ SetFde32(&this->memory_, 0x5200, 0xfc, 0, 0x50, 0x500);
+ // FDE 2 (0x00 - 0x800)
+ SetFde32(&this->memory_, 0x5300, 0xfc, 0, 0x0, 0x800);
+
+ this->debug_frame_->Init(0x5000, 0x400, 0);
+
+ // Force reading all entries so no entries are found.
+ const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
+ ASSERT_TRUE(fde == nullptr);
+
+ // 0x0 - 0x50 FDE 2
+ fde = this->debug_frame_->GetFdeFromPc(0x10);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x0U, fde->pc_start);
+ EXPECT_EQ(0x800U, fde->pc_end);
+
+ // 0x50 - 0x100 FDE 1
+ fde = this->debug_frame_->GetFdeFromPc(0x60);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x50U, fde->pc_start);
+ EXPECT_EQ(0x550U, fde->pc_end);
+
+ // 0x100 - 0x200 FDE 0
+ fde = this->debug_frame_->GetFdeFromPc(0x170);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x100U, fde->pc_start);
+ EXPECT_EQ(0x200U, fde->pc_end);
+
+ // 0x200 - 0x550 FDE 1
+ fde = this->debug_frame_->GetFdeFromPc(0x210);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x50U, fde->pc_start);
+ EXPECT_EQ(0x550U, fde->pc_end);
+
+ // 0x550 - 0x800 FDE 2
+ fde = this->debug_frame_->GetFdeFromPc(0x580);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x0U, fde->pc_start);
+ EXPECT_EQ(0x800U, fde->pc_end);
+
+ fde = this->debug_frame_->GetFdeFromPc(0x810);
+ ASSERT_TRUE(fde == nullptr);
+}
+
+REGISTER_TYPED_TEST_SUITE_P(
+ DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
+ GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
+ GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
+ GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
+ GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
+ GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
+ GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
+ GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
+ GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
+ GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved, GetFdeFromPc_overlap);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
new file mode 100644
index 000000000..46a25a480
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfEhFrameTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ eh_frame_ = new DwarfEhFrame<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete eh_frame_; }
+
+ MemoryFake memory_;
+ DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_SUITE_P(DwarfEhFrameTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+// Only verify different cie/fde format. All other DwarfSection corner
+// cases are tested in DwarfDebugFrameTest.cpp.
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0x5000, 0xfc);
+ // Indicates this is a cie for eh_frame.
+ this->memory_.SetData32(0x5004, 0);
+ this->memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 16, 32, 1});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x5100, 0xfc);
+ this->memory_.SetData32(0x5104, 0x104);
+ this->memory_.SetData32(0x5108, 0x1500);
+ this->memory_.SetData32(0x510c, 0x200);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x5000U, fde->cie_offset);
+ EXPECT_EQ(0x5110U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x5200U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x6608U, fde->pc_start);
+ EXPECT_EQ(0x6808U, fde->pc_end);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ const DwarfCie* cie = fde->cie;
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5100U, cie->cfa_instructions_end);
+ EXPECT_EQ(16U, cie->code_alignment_factor);
+ EXPECT_EQ(32U, cie->data_alignment_factor);
+ EXPECT_EQ(1U, cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x5000, 0xffffffff);
+ this->memory_.SetData64(0x5004, 0xfc);
+ // Indicates this is a cie for eh_frame.
+ this->memory_.SetData64(0x500c, 0);
+ this->memory_.SetMemory(0x5014, std::vector<uint8_t>{1, '\0', 16, 32, 1});
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x5100, 0xffffffff);
+ this->memory_.SetData64(0x5104, 0xfc);
+ this->memory_.SetData64(0x510c, 0x10c);
+ this->memory_.SetData64(0x5114, 0x1500);
+ this->memory_.SetData64(0x511c, 0x200);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x5000U, fde->cie_offset);
+ EXPECT_EQ(0x5124U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x5208U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x6618U, fde->pc_start);
+ EXPECT_EQ(0x6818U, fde->pc_end);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ const DwarfCie* cie = fde->cie;
+ ASSERT_TRUE(cie != nullptr);
+ EXPECT_EQ(1U, cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+ EXPECT_EQ(0U, cie->segment_size);
+ EXPECT_EQ('\0', cie->augmentation_string[0]);
+ EXPECT_EQ(0U, cie->personality_handler);
+ EXPECT_EQ(0x5019U, cie->cfa_instructions_offset);
+ EXPECT_EQ(0x5108U, cie->cfa_instructions_end);
+ EXPECT_EQ(16U, cie->code_alignment_factor);
+ EXPECT_EQ(32U, cie->data_alignment_factor);
+ EXPECT_EQ(1U, cie->return_address_register);
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
new file mode 100644
index 000000000..6aa386764
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+
+#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class TestDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+ public:
+ TestDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
+ ~TestDwarfEhFrameWithHdr() = default;
+
+ void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+ void TestSetHdrEntriesOffset(uint64_t offset) { this->hdr_entries_offset_ = offset; }
+ void TestSetHdrEntriesDataOffset(uint64_t offset) { this->hdr_entries_data_offset_ = offset; }
+ void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+ void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+ void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo& info) {
+ this->fde_info_[index] = info;
+ }
+
+ uint8_t TestGetVersion() { return this->version_; }
+ uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+ uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+ uint64_t TestGetFdeCount() { return this->fde_count_; }
+ uint64_t TestGetHdrEntriesOffset() { return this->hdr_entries_offset_; }
+ uint64_t TestGetHdrEntriesDataOffset() { return this->hdr_entries_data_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameWithHdrTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ eh_frame_ = new TestDwarfEhFrameWithHdr<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete eh_frame_; }
+
+ MemoryFake memory_;
+ TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init) {
+ this->memory_.SetMemory(
+ 0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 126);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+
+ // Verify a zero table entry size fails to init.
+ this->memory_.SetData8(0x1003, 0x1);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
+ // Reset the value back to the original.
+ this->memory_.SetData8(0x1003, DW_EH_PE_sdata4);
+
+ // Verify a zero fde count fails to init.
+ this->memory_.SetData32(0x1006, 0);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
+
+ // Verify an unexpected version will cause a fail.
+ this->memory_.SetData32(0x1006, 126);
+ this->memory_.SetData8(0x1000, 0);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
+ this->memory_.SetData8(0x1000, 2);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x10f8);
+ this->memory_.SetData32(0x140c, 0x200);
+ this->memory_.SetData16(0x1410, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x2000));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+ EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias_different_from_eh_frame_bias) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x20f8);
+ this->memory_.SetData32(0x140c, 0x200);
+ this->memory_.SetData16(0x1410, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x1000));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+ EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+ EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+ EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+ EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+ EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
+ EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+ EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_wtih_empty_fde) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x30f8);
+ this->memory_.SetData32(0x140c, 0);
+ this->memory_.SetData16(0x1410, 0);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1500, 0xfc);
+ this->memory_.SetData32(0x1504, 0x204);
+ this->memory_.SetData32(0x1508, 0x2ff8);
+ this->memory_.SetData32(0x150c, 0x200);
+ this->memory_.SetData16(0x1510, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x4500U, fde->pc_start);
+ EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes_with_empty_fde) {
+ this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+ DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 1);
+ this->memory_.SetData32(0x100a, 0x2500);
+ this->memory_.SetData32(0x100e, 0x1400);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x30f8);
+ this->memory_.SetData32(0x140c, 0);
+ this->memory_.SetData16(0x1410, 0);
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x1500, 0xfc);
+ this->memory_.SetData32(0x1504, 0x204);
+ this->memory_.SetData32(0x1508, 0x2ff8);
+ this->memory_.SetData32(0x150c, 0x200);
+ this->memory_.SetData16(0x1510, 0);
+
+ ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->eh_frame_->GetFdes(&fdes);
+ ASSERT_FALSE(fdes.empty());
+ ASSERT_EQ(1U, fdes.size());
+ EXPECT_EQ(0x4500U, fdes[0]->pc_start);
+ EXPECT_EQ(0x4700U, fdes[0]->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes) {
+ this->memory_.SetMemory(
+ 0x1000, std::vector<uint8_t>{1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+ this->memory_.SetData16(0x1004, 0x500);
+ this->memory_.SetData32(0x1006, 4);
+
+ // Header information.
+ this->memory_.SetData32(0x100a, 0x4600);
+ this->memory_.SetData32(0x100e, 0x1500);
+ this->memory_.SetData32(0x1012, 0x5500);
+ this->memory_.SetData32(0x1016, 0x1400);
+ this->memory_.SetData32(0x101a, 0x6800);
+ this->memory_.SetData32(0x101e, 0x1700);
+ this->memory_.SetData32(0x1022, 0x7700);
+ this->memory_.SetData32(0x1026, 0x1600);
+
+ // CIE 32 information.
+ this->memory_.SetData32(0x1300, 0xfc);
+ this->memory_.SetData32(0x1304, 0);
+ this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, '\0', 0, 0, 0});
+
+ // FDE 32 information.
+ // pc 0x5500 - 0x5700
+ this->memory_.SetData32(0x1400, 0xfc);
+ this->memory_.SetData32(0x1404, 0x104);
+ this->memory_.SetData32(0x1408, 0x40f8);
+ this->memory_.SetData32(0x140c, 0x200);
+
+ // pc 0x4600 - 0x4800
+ this->memory_.SetData32(0x1500, 0xfc);
+ this->memory_.SetData32(0x1504, 0x204);
+ this->memory_.SetData32(0x1508, 0x30f8);
+ this->memory_.SetData32(0x150c, 0x200);
+
+ // pc 0x7700 - 0x7900
+ this->memory_.SetData32(0x1600, 0xfc);
+ this->memory_.SetData32(0x1604, 0x304);
+ this->memory_.SetData32(0x1608, 0x60f8);
+ this->memory_.SetData32(0x160c, 0x200);
+
+ // pc 0x6800 - 0x6a00
+ this->memory_.SetData32(0x1700, 0xfc);
+ this->memory_.SetData32(0x1704, 0x404);
+ this->memory_.SetData32(0x1708, 0x50f8);
+ this->memory_.SetData32(0x170c, 0x200);
+
+ ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->eh_frame_->GetFdes(&fdes);
+ ASSERT_EQ(4U, fdes.size());
+
+ EXPECT_EQ(0x4600U, fdes[0]->pc_start);
+ EXPECT_EQ(0x4800U, fdes[0]->pc_end);
+ EXPECT_EQ(0x5500U, fdes[1]->pc_start);
+ EXPECT_EQ(0x5700U, fdes[1]->pc_end);
+ EXPECT_EQ(0x6800U, fdes[2]->pc_start);
+ EXPECT_EQ(0x6a00U, fdes[2]->pc_end);
+ EXPECT_EQ(0x7700U, fdes[3]->pc_start);
+ EXPECT_EQ(0x7900U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
+ EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
+ ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
+ EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
+}
+
+// We are assuming that pc rel, is really relative to the load_bias.
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+ this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+ this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x3340U, info->pc);
+ EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_cached) {
+ this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+ this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ this->memory_.SetData32(0x1040, 0x340);
+ this->memory_.SetData32(0x1044, 0x500);
+
+ auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+
+ // Clear the memory so that this will fail if it doesn't read cached data.
+ this->memory_.Clear();
+
+ info = this->eh_frame_->GetFdeInfoFromIndex(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_verify) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+
+ typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+ for (size_t i = 0; i < 10; i++) {
+ info.pc = 0x1000 * (i + 1);
+ info.offset = 0x5000 + i * 0x20;
+ this->eh_frame_->TestSetFdeInfo(i, info);
+ }
+
+ uint64_t fde_offset;
+ this->eh_frame_->TestSetFdeCount(10);
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ // Not an error, just not found.
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+ // Even number of elements.
+ for (size_t i = 0; i < 10; i++) {
+ SCOPED_TRACE(testing::Message() << "Failed at index " << i);
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ }
+
+ // Odd number of elements.
+ this->eh_frame_->TestSetFdeCount(9);
+ for (size_t i = 0; i < 9; i++) {
+ SCOPED_TRACE(testing::Message() << "Failed at index " << i);
+ TypeParam pc = 0x1000 * (i + 1);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ }
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_index_fail) {
+ this->eh_frame_->TestSetTableEntrySize(0x10);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ uint64_t fde_offset;
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
+ this->eh_frame_->TestSetFdeCount(0);
+
+ uint64_t fde_offset;
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_search) {
+ this->eh_frame_->TestSetTableEntrySize(16);
+ this->eh_frame_->TestSetFdeCount(10);
+
+ typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+ info.pc = 0x550;
+ info.offset = 0x10500;
+ this->eh_frame_->TestSetFdeInfo(5, info);
+ info.pc = 0x750;
+ info.offset = 0x10700;
+ this->eh_frame_->TestSetFdeInfo(7, info);
+ info.pc = 0x850;
+ info.offset = 0x10800;
+ this->eh_frame_->TestSetFdeInfo(8, info);
+
+ uint64_t fde_offset;
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+ EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
+ // CIE 32 information.
+ this->memory_.SetData32(0xf000, 0x100);
+ this->memory_.SetData32(0xf004, 0);
+ this->memory_.SetMemory(0xf008, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+
+ // FDE 32 information.
+ this->memory_.SetData32(0x14000, 0x20);
+ this->memory_.SetData32(0x14004, 0x5004);
+ this->memory_.SetData32(0x14008, 0x9000);
+ this->memory_.SetData32(0x1400c, 0x100);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+ EXPECT_EQ(0x1d008U, fde->pc_start);
+ EXPECT_EQ(0x1d108U, fde->pc_end);
+ EXPECT_EQ(0xf000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde64) {
+ // CIE 64 information.
+ this->memory_.SetData32(0x6000, 0xffffffff);
+ this->memory_.SetData64(0x6004, 0x100);
+ this->memory_.SetData64(0x600c, 0);
+ this->memory_.SetMemory(0x6014, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+
+ // FDE 64 information.
+ this->memory_.SetData32(0x8000, 0xffffffff);
+ this->memory_.SetData64(0x8004, 0x200);
+ this->memory_.SetData64(0x800c, 0x200c);
+ this->memory_.SetData64(0x8014, 0x5000);
+ this->memory_.SetData64(0x801c, 0x300);
+
+ const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+ ASSERT_TRUE(fde != nullptr);
+ EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+ EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+ EXPECT_EQ(0xd018U, fde->pc_start);
+ EXPECT_EQ(0xd318U, fde->pc_end);
+ EXPECT_EQ(0x6000U, fde->cie_offset);
+ EXPECT_EQ(0U, fde->lsda_address);
+
+ ASSERT_TRUE(fde->cie != nullptr);
+ EXPECT_EQ(1U, fde->cie->version);
+ EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+ EXPECT_EQ(0U, fde->cie->segment_size);
+ EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+ EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+ EXPECT_EQ(0U, fde->cie->personality_handler);
+ EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+ EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+ EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+ EXPECT_EQ(8, fde->cie->data_alignment_factor);
+ EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_fde_not_found) {
+ this->eh_frame_->TestSetTableEntrySize(16);
+ this->eh_frame_->TestSetFdeCount(1);
+
+ typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+ info.pc = 0x550;
+ info.offset = 0x10500;
+ this->eh_frame_->TestSetFdeInfo(0, info);
+
+ ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
+ Init_non_zero_load_bias_different_from_eh_frame_bias,
+ GetFdeFromPc_wtih_empty_fde, GetFdes_with_empty_fde, GetFdes,
+ GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+ GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+ GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
+ GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
+ GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 000000000..650e965b2
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfMemory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DwarfMemoryTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ dwarf_mem_.reset(new DwarfMemory(&memory_));
+ }
+
+ template <typename AddressType>
+ void GetEncodedSizeTest(uint8_t value, size_t expected);
+ template <typename AddressType>
+ void ReadEncodedValue_omit();
+ template <typename AddressType>
+ void ReadEncodedValue_leb128();
+ template <typename AddressType>
+ void ReadEncodedValue_data1();
+ template <typename AddressType>
+ void ReadEncodedValue_data2();
+ template <typename AddressType>
+ void ReadEncodedValue_data4();
+ template <typename AddressType>
+ void ReadEncodedValue_data8();
+ template <typename AddressType>
+ void ReadEncodedValue_non_zero_adjust();
+ template <typename AddressType>
+ void ReadEncodedValue_overflow();
+ template <typename AddressType>
+ void ReadEncodedValue_high_bit_set();
+ template <typename AddressType>
+ void ReadEncodedValue_all();
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dwarf_mem_;
+};
+
+TEST_F(DwarfMemoryTest, ReadBytes) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x10, 0x18, 0xff, 0xfe});
+
+ uint8_t byte;
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x10U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x18U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xfeU, byte);
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+
+ dwarf_mem_->set_cur_offset(2);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+}
+
+TEST_F(DwarfMemoryTest, ReadSigned_check) {
+ uint64_t value;
+
+ // Signed 8 byte reads.
+ memory_.SetData8(0, static_cast<uint8_t>(-10));
+ memory_.SetData8(1, 200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(-10), static_cast<int8_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(200), static_cast<int8_t>(value));
+
+ // Signed 16 byte reads.
+ memory_.SetData16(0x10, static_cast<uint16_t>(-1000));
+ memory_.SetData16(0x12, 50100);
+ dwarf_mem_->set_cur_offset(0x10);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(-1000), static_cast<int16_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(50100), static_cast<int16_t>(value));
+
+ // Signed 32 byte reads.
+ memory_.SetData32(0x100, static_cast<uint32_t>(-1000000000));
+ memory_.SetData32(0x104, 3000000000);
+ dwarf_mem_->set_cur_offset(0x100);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(-1000000000), static_cast<int32_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(3000000000), static_cast<int32_t>(value));
+
+ // Signed 64 byte reads.
+ memory_.SetData64(0x200, static_cast<uint64_t>(-2000000000000LL));
+ memory_.SetData64(0x208, 5000000000000LL);
+ dwarf_mem_->set_cur_offset(0x200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(-2000000000000), static_cast<int64_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(5000000000000), static_cast<int64_t>(value));
+}
+
+TEST_F(DwarfMemoryTest, ReadULEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f});
+
+ uint64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(1U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1200U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(7U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffe1ffU, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadSLEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f,
+ 0x2f, 0xbf, 0xc3, 0xf7, 0x5f});
+
+ int64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(6U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(2U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xffffffffffffffc0ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1a02U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(6U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffffff209ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(10U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x5e3e1f9U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(14U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast<uint64_t>(value));
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) {
+ for (size_t i = 0; i < 16; i++) {
+ uint8_t encoding = (i << 4) | value;
+ ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize<AddressType>(encoding))
+ << "encoding 0x" << std::hex << static_cast<uint32_t>(encoding) << " test value 0x"
+ << static_cast<size_t>(value);
+ }
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) {
+ GetEncodedSizeTest<uint32_t>(0, sizeof(uint32_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) {
+ GetEncodedSizeTest<uint64_t>(0, sizeof(uint64_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data1) {
+ // udata1
+ GetEncodedSizeTest<uint32_t>(0x0d, 1);
+ GetEncodedSizeTest<uint64_t>(0x0d, 1);
+
+ // sdata1
+ GetEncodedSizeTest<uint32_t>(0x0e, 1);
+ GetEncodedSizeTest<uint64_t>(0x0e, 1);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data2) {
+ // udata2
+ GetEncodedSizeTest<uint32_t>(0x02, 2);
+ GetEncodedSizeTest<uint64_t>(0x02, 2);
+
+ // sdata2
+ GetEncodedSizeTest<uint32_t>(0x0a, 2);
+ GetEncodedSizeTest<uint64_t>(0x0a, 2);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data4) {
+ // udata4
+ GetEncodedSizeTest<uint32_t>(0x03, 4);
+ GetEncodedSizeTest<uint64_t>(0x03, 4);
+
+ // sdata4
+ GetEncodedSizeTest<uint32_t>(0x0b, 4);
+ GetEncodedSizeTest<uint64_t>(0x0b, 4);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data8) {
+ // udata8
+ GetEncodedSizeTest<uint32_t>(0x04, 8);
+ GetEncodedSizeTest<uint64_t>(0x04, 8);
+
+ // sdata8
+ GetEncodedSizeTest<uint32_t>(0x0c, 8);
+ GetEncodedSizeTest<uint64_t>(0x0c, 8);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) {
+ GetEncodedSizeTest<uint32_t>(0x01, 0);
+ GetEncodedSizeTest<uint64_t>(0x01, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x09, 0);
+ GetEncodedSizeTest<uint64_t>(0x09, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x0f, 0);
+ GetEncodedSizeTest<uint64_t>(0x0f, 0);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_omit() {
+ uint64_t value = 123;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xff, &value));
+ ASSERT_EQ(0U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+ ReadEncodedValue_omit<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+ ReadEncodedValue_omit<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+
+ memory_.SetData32(0, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+
+ memory_.SetData64(0, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+
+ memory_.SetData32(4, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+
+ memory_.SetData64(8, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+ ASSERT_EQ(16U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_leb128() {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x80, 0x42});
+
+ uint64_t value = 100;
+ // uleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x01, &value));
+ ASSERT_EQ(0x2100U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ // sleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x09, &value));
+ ASSERT_EQ(0xffffffffffffe100ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+ ReadEncodedValue_leb128<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+ ReadEncodedValue_leb128<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data1() {
+ memory_.SetData8(0, 0xe0);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0d, &value));
+ ASSERT_EQ(0xe0U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0e, &value));
+ ASSERT_EQ(0xffffffffffffffe0ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+ ReadEncodedValue_data1<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+ ReadEncodedValue_data1<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data2() {
+ memory_.SetData16(0, 0xe000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x02, &value));
+ ASSERT_EQ(0xe000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0a, &value));
+ ASSERT_EQ(0xffffffffffffe000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+ ReadEncodedValue_data2<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+ ReadEncodedValue_data2<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data4() {
+ memory_.SetData32(0, 0xe0000000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x03, &value));
+ ASSERT_EQ(0xe0000000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0b, &value));
+ ASSERT_EQ(0xffffffffe0000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+ ReadEncodedValue_data4<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+ ReadEncodedValue_data4<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data8() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x04, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0c, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+ ReadEncodedValue_data8<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+ ReadEncodedValue_data8<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x14, &value));
+ ASSERT_EQ(0xe000000000002000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) {
+ ReadEncodedValue_non_zero_adjust<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) {
+ ReadEncodedValue_non_zero_adjust<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_overflow() {
+ memory_.SetData64(0, 0);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_cur_offset(UINT64_MAX);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0x50, &value));
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) {
+ ReadEncodedValue_overflow<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) {
+ ReadEncodedValue_overflow<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_high_bit_set() {
+ uint64_t value;
+ memory_.SetData32(0, 0x15234);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+ ASSERT_EQ(0x75234U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint32_t) {
+ ReadEncodedValue_high_bit_set<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint64_t) {
+ ReadEncodedValue_high_bit_set<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_all() {
+ MemoryFakeAlwaysReadZero memory;
+ DwarfMemory dwarf_mem(&memory);
+
+ for (size_t i = 0; i <= 0xff; i++) {
+ uint64_t value;
+ if (dwarf_mem.ReadEncodedValue<AddressType>(static_cast<uint8_t>(i), &value)) {
+ ASSERT_EQ(0U, value);
+ }
+ }
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_all_uint32_t) {
+ ReadEncodedValue_all<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_all_uint64_t) {
+ ReadEncodedValue_all<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
+ uint64_t value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
+ ASSERT_EQ(0x1234U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) {
+ uint64_t value = 0x1234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x3234U, value);
+
+ dwarf_mem_->set_pc_offset(static_cast<uint64_t>(-4));
+ value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x1230U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) {
+ uint64_t value = 0x8234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+
+ dwarf_mem_->set_text_offset(0x1000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x9234U, value);
+
+ dwarf_mem_->set_text_offset(static_cast<uint64_t>(-16));
+ value = 0x8234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x8224U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) {
+ uint64_t value = 0xb234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+
+ dwarf_mem_->set_data_offset(0x1200);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xc434U, value);
+
+ dwarf_mem_->set_data_offset(static_cast<uint64_t>(-256));
+ value = 0xb234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xb134U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) {
+ uint64_t value = 0x15234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x75234U, value);
+
+ dwarf_mem_->set_func_offset(static_cast<uint64_t>(-4096));
+ value = 0x15234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x14234U, value);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 000000000..8dbf6e815
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
+#include "DwarfOp.h"
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfOpLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_SUITE_P(DwarfOpLogTest);
+
+TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ std::vector<std::string> lines;
+ this->op_->GetLogInfo(0, opcode_buffer.size(), &lines);
+ std::vector<std::string> expected{
+ "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03",
+ "DW_OP_dup", "Raw Data: 0x12", "DW_OP_xor", "Raw Data: 0x27"};
+ ASSERT_EQ(expected, lines);
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpLogTest, multiple_ops);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfOpLogTest, DwarfOpLogTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 000000000..0e2d91a8a
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1586 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+
+#include "DwarfOp.h"
+
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfOpTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), &regular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_SUITE_P(DwarfOpTest);
+
+TYPED_TEST_P(DwarfOpTest, decode) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+ EXPECT_EQ(0U, this->op_->LastErrorAddress());
+
+ // No error.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+ this->mem_->set_cur_offset(0);
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
+ ASSERT_EQ(0x96U, this->op_->cur_op());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, eval) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Eval(0, 2));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+ EXPECT_EQ(0U, this->op_->LastErrorAddress());
+
+ // Register set.
+ // Do this first, to verify that subsequent calls reset the value.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
+ ASSERT_TRUE(this->op_->Eval(0, 1));
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 8));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(8U, this->mem_->cur_offset());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(1U, this->op_->StackAt(0));
+ ASSERT_EQ(2U, this->op_->StackAt(1));
+ ASSERT_EQ(3U, this->op_->StackAt(2));
+ ASSERT_EQ(4U, this->op_->StackAt(3));
+
+ // Infinite loop.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
+ ASSERT_FALSE(this->op_->Eval(0, 4));
+ ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->LastErrorCode());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
+ // Fill the buffer with all of the illegal opcodes.
+ std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
+ for (size_t opcode = 0xa0; opcode < 256; opcode++) {
+ opcode_buffer.push_back(opcode);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, not_implemented) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push values so that any not implemented ops will return the right error.
+ 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ // xderef
+ 0x18,
+ // fbreg
+ 0x91, 0x01,
+ // piece
+ 0x93, 0x01,
+ // xderef_size
+ 0x95, 0x01,
+ // push_object_address
+ 0x97,
+ // call2
+ 0x98, 0x01, 0x02,
+ // call4
+ 0x99, 0x01, 0x02, 0x03, 0x04,
+ // call_ref
+ 0x9a,
+ // form_tls_address
+ 0x9b,
+ // call_frame_cfa
+ 0x9c,
+ // bit_piece
+ 0x9d, 0x01, 0x01,
+ // implicit_value
+ 0x9e, 0x01,
+ // stack_value
+ 0x9f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Push the stack values.
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_TRUE(this->op_->Decode());
+
+ while (this->mem_->cur_offset() < opcode_buffer.size()) {
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->LastErrorCode());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_addr) {
+ std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
+ if (sizeof(TypeParam) == 8) {
+ opcode_buffer.push_back(0x56);
+ opcode_buffer.push_back(0x67);
+ opcode_buffer.push_back(0x78);
+ opcode_buffer.push_back(0x89);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x03, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Try a dereference with nothing on the stack.
+ 0x06,
+ // Add an address, then dereference.
+ 0x0a, 0x10, 0x20, 0x06,
+ // Now do another dereference that should fail in memory.
+ 0x06,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x06, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(value, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+ ASSERT_EQ(0x12345678U, this->op_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref_size) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ // Read all byte sizes up to the sizeof the type.
+ for (size_t i = 1; i < sizeof(TypeParam); i++) {
+ this->op_memory_.SetMemory(
+ 0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
+ ASSERT_TRUE(this->op_->Eval(0, 5)) << "Failed at size " << i;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
+ ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
+ TypeParam expected_value = 0;
+ memcpy(&expected_value, &value, i);
+ ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
+ }
+
+ // Zero byte read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
+ ASSERT_FALSE(this->op_->Eval(0, 5));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+
+ // Read too many bytes.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
+ ASSERT_FALSE(this->op_->Eval(0, 5));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+
+ // Force bad memory read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
+ ASSERT_FALSE(this->op_->Eval(0, 5));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+ EXPECT_EQ(0x4010U, this->op_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfOpTest, const_unsigned) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1u
+ 0x08, 0x12, 0x08, 0xff,
+ // const2u
+ 0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
+ // const4u
+ 0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
+ // const8u
+ 0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
+ 0xdc, 0xed, 0xfe,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1u
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xffU, this->op_->StackAt(0));
+
+ // const2u
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1245U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xff00U, this->op_->StackAt(0));
+
+ // const4u
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
+
+ // const8u
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_signed) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1s
+ 0x09, 0x12, 0x09, 0xff,
+ // const2s
+ 0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
+ // const4s
+ 0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
+ // const8s
+ 0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
+ 0xef, 0xef, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1s
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // const2s
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x3221U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
+
+ // const4s
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
+
+ // const8s
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_uleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte ULEB128
+ 0x10, 0x22, 0x10, 0x7f,
+ // Multi byte ULEB128
+ 0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte ULEB128
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x7fU, this->op_->StackAt(0));
+
+ // Multi byte ULEB128
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_sleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte SLEB128
+ 0x11, 0x22, 0x11, 0x7f,
+ // Multi byte SLEB128
+ 0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x11,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0xb8);
+ opcode_buffer.push_back(0xd3);
+ opcode_buffer.push_back(0x63);
+ } else {
+ opcode_buffer.push_back(0x81);
+ opcode_buffer.push_back(0x82);
+ opcode_buffer.push_back(0x83);
+ opcode_buffer.push_back(0x84);
+ opcode_buffer.push_back(0x85);
+ opcode_buffer.push_back(0x86);
+ opcode_buffer.push_back(0x87);
+ opcode_buffer.push_back(0x88);
+ opcode_buffer.push_back(0x79);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte SLEB128
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // Multi byte SLEB128
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_dup) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Should fail since nothing is on the stack.
+ 0x12,
+ // Push on a value and dup.
+ 0x08, 0x15, 0x12,
+ // Do it again.
+ 0x08, 0x23, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x15U, this->op_->StackAt(0));
+ ASSERT_EQ(0x15U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x23U, this->op_->StackAt(0));
+ ASSERT_EQ(0x23U, this->op_->StackAt(1));
+ ASSERT_EQ(0x15U, this->op_->StackAt(2));
+ ASSERT_EQ(0x15U, this->op_->StackAt(3));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_drop) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x10, 0x08, 0x20,
+ // Drop the values.
+ 0x13, 0x13,
+ // Attempt to drop empty stack.
+ 0x13,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_over) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x1a, 0x08, 0xed,
+ // Copy a value.
+ 0x14,
+ // Remove all but one element.
+ 0x13, 0x13,
+ // Provoke a failure with this opcode.
+ 0x14,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_pick) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a few values.
+ 0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
+ // Copy the value at offset 2.
+ 0x15, 0x01,
+ // Copy the last value in the stack.
+ 0x15, 0x03,
+ // Choose an invalid index.
+ 0x15, 0x10,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xedU, this->op_->StackAt(0));
+ ASSERT_EQ(0x34U, this->op_->StackAt(1));
+ ASSERT_EQ(0xedU, this->op_->StackAt(2));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(3));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x34U, this->op_->StackAt(2));
+ ASSERT_EQ(0xedU, this->op_->StackAt(3));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(4));
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_swap) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x26, 0x08, 0xab,
+ // Swap values.
+ 0x16,
+ // Pop a value to cause a failure.
+ 0x13, 0x16,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xabU, this->op_->StackAt(0));
+ ASSERT_EQ(0x26U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x26U, this->op_->StackAt(0));
+ ASSERT_EQ(0xabU, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_rot) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Rotate that should cause a failure.
+ 0x17, 0x08, 0x10,
+ // Only 1 value on stack, should fail.
+ 0x17, 0x08, 0x20,
+ // Only 2 values on stack, should fail.
+ 0x17, 0x08, 0x30,
+ // Should rotate properly.
+ 0x17,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x30U, this->op_->StackAt(0));
+ ASSERT_EQ(0x20U, this->op_->StackAt(1));
+ ASSERT_EQ(0x10U, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x17, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x20U, this->op_->StackAt(0));
+ ASSERT_EQ(0x10U, this->op_->StackAt(1));
+ ASSERT_EQ(0x30U, this->op_->StackAt(2));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_abs) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Abs that should fail.
+ 0x19,
+ // A value that is already positive.
+ 0x08, 0x10, 0x19,
+ // A value that is negative.
+ 0x11, 0x7f, 0x19,
+ // A value that is large and negative.
+ 0x11, 0x81, 0x80, 0x80, 0x80,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0x08);
+ } else {
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x01);
+ }
+ opcode_buffer.push_back(0x19);
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x1U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(2147483647U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_and) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1b,
+ // Push a single value.
+ 0x08, 0x20,
+ // One element stack, and op will fail.
+ 0x1b,
+ // Push another value.
+ 0x08, 0x02, 0x1b,
+ // Push on two negative values.
+ 0x11, 0x7c, 0x11, 0x7f, 0x1b,
+ // Push one negative, one positive.
+ 0x11, 0x10, 0x11, 0x7c, 0x1b,
+ // Divide by zero.
+ 0x11, 0x10, 0x11, 0x00, 0x1b,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ // Two positive values.
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ // Two negative values.
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x04U, this->op_->StackAt(0));
+
+ // One negative value, one positive value.
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
+
+ // Divide by zero.
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(5U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_div) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1a,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1a,
+ // Push another value.
+ 0x08, 0xf0, 0x1a,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1a, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x40U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_minus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1c,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1c,
+ // Push another value.
+ 0x08, 0x04, 0x1c,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1c, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x44U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mod) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1d,
+ // Push a single value.
+ 0x08, 0x47,
+ // One element stack, and op will fail.
+ 0x1d,
+ // Push another value.
+ 0x08, 0x04, 0x1d,
+ // Try a mod of zero.
+ 0x08, 0x01, 0x08, 0x00, 0x1d,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1d, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mul) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1e,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1e,
+ // Push another value.
+ 0x08, 0x04, 0x1e,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1e, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x120U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_neg) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1f,
+ // Push a single value.
+ 0x08, 0x48, 0x1f,
+ // Push a negative value.
+ 0x11, 0x7f, 0x1f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x01U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_not) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x20,
+ // Push a single value.
+ 0x08, 0x4, 0x20,
+ // Push a negative value.
+ 0x11, 0x7c, 0x20,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_or) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x21,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x21,
+ // Push another value.
+ 0x08, 0xf4, 0x21,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x21, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xfcU, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x22,
+ // Push a single value.
+ 0x08, 0xff,
+ // One element stack, and op will fail.
+ 0x22,
+ // Push another value.
+ 0x08, 0xf2, 0x22,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x22, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x23,
+ // Push a single value.
+ 0x08, 0x50, 0x23, 0x80, 0x51,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x23, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shl) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x24,
+ // Push a single value.
+ 0x08, 0x67,
+ // One element stack, and op will fail.
+ 0x24,
+ // Push another value.
+ 0x08, 0x03, 0x24,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x24, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x338U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shr) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x25,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x25,
+ // Push another value.
+ 0x08, 0x03, 0x25,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x25, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x26,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x26,
+ // Push another value.
+ 0x08, 0x03, 0x26,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x26, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_xor) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x27,
+ // Push a single value.
+ 0x08, 0x11,
+ // One element stack, and op will fail.
+ 0x27,
+ // Push another value.
+ 0x08, 0x41, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x27, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x50U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x28,
+ // Push on a non-zero value with a positive branch.
+ 0x08, 0x11, 0x28, 0x02, 0x01,
+ // Push on a zero value with a positive branch.
+ 0x08, 0x00, 0x28, 0x05, 0x00,
+ // Push on a non-zero value with a negative branch.
+ 0x08, 0x11, 0x28, 0xfc, 0xff,
+ // Push on a zero value with a negative branch.
+ 0x08, 0x00, 0x28, 0xf0, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ // Push on a non-zero value with a positive branch.
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
+
+ // Push on a zero value with a positive branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 5, this->mem_->cur_offset());
+
+ // Push on a non-zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 4, this->mem_->cur_offset());
+
+ // Push on a zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 16, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
+ // All of the ops require two stack elements. Loop through all of these
+ // ops with potential errors.
+ std::vector<uint8_t> opcode_buffer = {
+ 0xff, // Place holder for compare op.
+ 0x08, 0x11,
+ 0xff, // Place holder for compare op.
+ };
+
+ for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
+ opcode_buffer[0] = opcode;
+ opcode_buffer[3] = opcode;
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Eval(0, 1));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+
+ ASSERT_FALSE(this->op_->Eval(1, 4));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
+ // Have three different checks for each compare op:
+ // - Both values the same.
+ // - The first value larger than the second.
+ // - The second value larger than the first.
+ std::vector<uint8_t> opcode_buffer = {
+ // Values the same.
+ 0x08, 0x11, 0x08, 0x11,
+ 0xff, // Placeholder.
+ // First value larger.
+ 0x08, 0x12, 0x08, 0x10,
+ 0xff, // Placeholder.
+ // Second value larger.
+ 0x08, 0x10, 0x08, 0x12,
+ 0xff, // Placeholder.
+ };
+
+ // Opcode followed by the expected values on the stack.
+ std::vector<uint8_t> expected = {
+ 0x29, 1, 0, 0, // eq
+ 0x2a, 1, 1, 0, // ge
+ 0x2b, 0, 1, 0, // gt
+ 0x2c, 1, 0, 1, // le
+ 0x2d, 0, 0, 1, // lt
+ 0x2e, 0, 1, 1, // ne
+ };
+ for (size_t i = 0; i < expected.size(); i += 4) {
+ opcode_buffer[4] = expected[i];
+ opcode_buffer[9] = expected[i];
+ opcode_buffer[14] = expected[i];
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 15))
+ << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
+
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
+ ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
+ ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_skip) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Positive value.
+ 0x2f, 0x10, 0x20,
+ // Negative value.
+ 0x2f, 0xfd, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
+
+ this->mem_->set_cur_offset(offset);
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 3, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_lit) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every lit opcode.
+ for (uint8_t op = 0x30; op <= 0x4f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_reg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x50; op <= 0x6f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_regx) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x90, 0x02, 0x90, 0x80, 0x15,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 2));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x02U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(2, 5));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xa80U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x12);
+ // Negative value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x7e);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsImplFake<TypeParam> regs(32);
+ for (size_t i = 0; i < 32; i++) {
+ regs[i] = i + 10;
+ }
+ RegsInfo<TypeParam> regs_info(&regs);
+ this->op_->set_regs_info(&regs_info);
+
+ uint64_t offset = 0;
+ for (uint32_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+
+ // Negative value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x7f, 0x12, 0x80, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsImplFake<TypeParam> regs(16);
+ for (size_t i = 0; i < 16; i++) {
+ regs[i] = i + 10;
+ }
+ RegsInfo<TypeParam> regs_info(&regs);
+ this->op_->set_regs_info(&regs_info);
+
+ // Should pass since this references the last regsister.
+ ASSERT_TRUE(this->op_->Eval(0, 2));
+ ASSERT_EQ(0x7fU, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x2bU, this->op_->StackAt(0));
+
+ // Should fail since this references a non-existent register.
+ ASSERT_FALSE(this->op_->Eval(2, 4));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bregx) {
+ std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
+ 0x92, 0x05, 0x20,
+ // Negative value added to register.
+ 0x92, 0x06, 0x80, 0x7e,
+ // Illegal register.
+ 0x92, 0x80, 0x15, 0x80, 0x02};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsImplFake<TypeParam> regs(10);
+ regs[5] = 0x45;
+ regs[6] = 0x190;
+ RegsInfo<TypeParam> regs_info(&regs);
+ this->op_->set_regs_info(&regs_info);
+
+ ASSERT_TRUE(this->op_->Eval(0, 3));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x65U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(3, 7));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x90U, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Eval(7, 12));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_nop) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+
+ ASSERT_TRUE(this->op_->Decode());
+ ASSERT_EQ(0x96, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, is_dex_pc) {
+ // Special sequence that indicates this is a dex pc.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13});
+
+ ASSERT_TRUE(this->op_->Eval(0, 6));
+ EXPECT_TRUE(this->op_->dex_pc_set());
+
+ // Try without the last op.
+ ASSERT_TRUE(this->op_->Eval(0, 5));
+ EXPECT_FALSE(this->op_->dex_pc_set());
+
+ // Change the constant.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '2', 0x13});
+ ASSERT_TRUE(this->op_->Eval(0, 6));
+ EXPECT_FALSE(this->op_->dex_pc_set());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+ op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+ const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+ op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or,
+ op_plus, op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+ compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+ op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop,
+ is_dex_pc);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfOpTest, DwarfOpTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
new file mode 100644
index 000000000..cac59b7e9
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfSection.h>
+
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class TestDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+ public:
+ TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+ virtual ~TestDwarfSectionImpl() = default;
+
+ bool Init(uint64_t, uint64_t, int64_t) override { return false; }
+
+ void GetFdes(std::vector<const DwarfFde*>*) override {}
+
+ const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
+
+ uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
+
+ void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
+ this->cie_loc_regs_[offset] = loc_regs;
+ }
+ void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
+ void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
+};
+
+template <typename TypeParam>
+class DwarfSectionImplTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
+ ResetLogs();
+ }
+
+ void TearDown() override { delete section_; }
+
+ MemoryFake memory_;
+ TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
+};
+TYPED_TEST_SUITE_P(DwarfSectionImplTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache) {
+ ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+ EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+ EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+ ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+ EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+ this->section_->TestClearError();
+ ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+ EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+ EXPECT_EQ(0x5000U, this->section_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ TypeParam cfa_value = 0x12345;
+ this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ ASSERT_FALSE(finished);
+ EXPECT_EQ(0x80000000U, regs.sp());
+ EXPECT_EQ(0x20U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
+ DwarfCie cie{.return_address_register = 60};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+
+ this->section_->TestClearError();
+ loc_regs.erase(CFA_REG);
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x3000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[6] = 0x4000;
+ regs[9] = 0x3000;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x4000U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[1] = 0x100;
+ regs[3] = 0x300;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 1}};
+ loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(0x301U, regs[1]);
+ EXPECT_EQ(0x300U, regs[3]);
+ EXPECT_EQ(0x10U, regs[8]);
+ EXPECT_EQ(0x102U, regs[9]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_register_reference_chain) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[0] = 0x10;
+ regs[1] = 0x20;
+ regs[2] = 0x30;
+ regs[3] = 0x40;
+ regs[4] = 0x50;
+ regs[5] = 0x60;
+ regs[8] = 0x20;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
+ loc_regs[2] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
+ loc_regs[3] = DwarfLocation{DWARF_LOCATION_REGISTER, {2, 3}};
+ loc_regs[4] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 4}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 5}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(0x10U, regs[0]);
+ EXPECT_EQ(0x11U, regs[1]);
+ EXPECT_EQ(0x22U, regs[2]);
+ EXPECT_EQ(0x33U, regs[3]);
+ EXPECT_EQ(0x44U, regs[4]);
+ EXPECT_EQ(0x55U, regs[5]);
+ EXPECT_EQ(0x20U, regs[8]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_dex_pc) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[0] = 0x10;
+ regs[8] = 0x20;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x8, 0x5008}};
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13, 0x08, 0x11});
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(0x10U, regs[0]);
+ EXPECT_EQ(0x20U, regs[8]);
+ EXPECT_EQ(0x11U, regs.dex_pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ if (sizeof(TypeParam) == sizeof(uint64_t)) {
+ this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
+ } else {
+ this->memory_.SetData32(0x2150, 0x12345678);
+ }
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[3] = 0x234;
+ regs[5] = 0x10;
+ regs[8] = 0x2100;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
+ loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
+ loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x10U, regs.pc());
+ EXPECT_EQ(0x2100U, regs.sp());
+ EXPECT_EQ(0x2200U, regs[1]);
+ EXPECT_EQ(0x234U, regs[3]);
+ if (sizeof(TypeParam) == sizeof(uint64_t)) {
+ EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
+ } else {
+ EXPECT_EQ(0x12345678U, regs[2]);
+ }
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_TRUE(finished);
+ EXPECT_EQ(0U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pc_zero) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_TRUE(finished);
+ EXPECT_EQ(0U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[5] = 0x20;
+ regs[8] = 0x10;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ // This should not result in any errors.
+ loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x20U, regs.pc());
+ EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ TypeParam cfa_value = 0x12345;
+ this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x3000U, regs.sp());
+ EXPECT_EQ(0x12345U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
+ DwarfCie cie{.version = 3, .return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[8] = 0x3000;
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_FALSE(finished);
+ EXPECT_EQ(0x3000U, regs.sp());
+ EXPECT_EQ(0x80000000U, regs.pc());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x3000;
+ cie.cfa_instructions_end = 0x3002;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cie_offset = 0x8000;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6002;
+
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_EQ(2U, loc_regs.size());
+
+ auto entry = loc_regs.find(2);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(1U, entry->second.values[0]);
+
+ entry = loc_regs.find(4);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x3000;
+ cie.cfa_instructions_end = 0x3002;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cie_offset = 0x8000;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6002;
+
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[6] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 0}};
+ this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
+
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_EQ(2U, loc_regs.size());
+
+ auto entry = loc_regs.find(6);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(4U, entry->second.values[0]);
+
+ entry = loc_regs.find(4);
+ ASSERT_NE(entry, loc_regs.end());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
+ ASSERT_EQ(3U, entry->second.values[0]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Log) {
+ DwarfCie cie{};
+ cie.cfa_instructions_offset = 0x5000;
+ cie.cfa_instructions_end = 0x5001;
+ DwarfFde fde{};
+ fde.cie = &cie;
+ fde.cfa_instructions_offset = 0x6000;
+ fde.cfa_instructions_end = 0x6001;
+
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
+ this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
+
+ ASSERT_EQ(
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+ GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+ Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+ Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+ Eval_cfa_register_prev, Eval_cfa_register_from_value,
+ Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+ Eval_invalid_register, Eval_different_reg_locations,
+ Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+ Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
new file mode 100644
index 000000000..953dc7563
--- /dev/null
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MockDwarfSection : public DwarfSection {
+ public:
+ MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
+ virtual ~MockDwarfSection() = default;
+
+ MOCK_METHOD(bool, Init, (uint64_t, uint64_t, int64_t), (override));
+
+ MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
+ (override));
+
+ MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
+
+ MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
+
+ MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
+
+ MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
+
+ MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
+
+ MOCK_METHOD(uint64_t, GetCieOffsetFromFde64, (uint64_t), (override));
+
+ MOCK_METHOD(uint64_t, AdjustPcFromFde, (uint64_t), (override));
+};
+
+class DwarfSectionTest : public ::testing::Test {
+ protected:
+ void SetUp() override { section_.reset(new MockDwarfSection(&memory_)); }
+
+ MemoryFake memory_;
+ std::unique_ptr<MockDwarfSection> section_;
+};
+
+TEST_F(DwarfSectionTest, Step_fail_fde) {
+ EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
+
+ bool finished;
+ ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cie_null) {
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = nullptr;
+
+ EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+
+ bool finished;
+ ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ bool finished;
+ ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_pass) {
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Return(true));
+
+ MemoryFake process;
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ .WillOnce(::testing::Return(true));
+
+ bool finished;
+ ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+}
+
+static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
+ dwarf_loc_regs_t* loc_regs) {
+ loc_regs->pc_start = fde->pc_start;
+ loc_regs->pc_end = fde->pc_end;
+ return true;
+}
+
+TEST_F(DwarfSectionTest, Step_cache) {
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_start = 0x500;
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+ MemoryFake process;
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ .WillRepeatedly(::testing::Return(true));
+
+ bool finished;
+ ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
+ DwarfCie cie{};
+ DwarfFde fde0{};
+ fde0.pc_start = 0x1000;
+ fde0.pc_end = 0x2000;
+ fde0.cie = &cie;
+ EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+ .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+ MemoryFake process;
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ .WillRepeatedly(::testing::Return(true));
+
+ bool finished;
+ ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+
+ DwarfFde fde1{};
+ fde1.pc_start = 0x500;
+ fde1.pc_end = 0x800;
+ fde1.cie = &cie;
+ EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+ .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+ ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
new file mode 100644
index 000000000..5f135467d
--- /dev/null
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class ElfCacheTest : public ::testing::Test {
+ protected:
+ static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
+
+ void SetUp() override { Elf::SetCachingEnabled(true); }
+
+ void TearDown() override { Elf::SetCachingEnabled(false); }
+
+ void WriteElfFile(uint64_t offset, TemporaryFile* tf, uint32_t type) {
+ ASSERT_TRUE(type == EM_ARM || type == EM_386 || type == EM_X86_64);
+ size_t ehdr_size;
+ Elf32_Ehdr ehdr32;
+ Elf64_Ehdr ehdr64;
+ void* ptr;
+ if (type == EM_ARM || type == EM_386) {
+ ehdr_size = sizeof(ehdr32);
+ ptr = &ehdr32;
+ TestInitEhdr(&ehdr32, ELFCLASS32, type);
+ } else {
+ ehdr_size = sizeof(ehdr64);
+ ptr = &ehdr64;
+ TestInitEhdr(&ehdr64, ELFCLASS64, type);
+ }
+
+ ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET)));
+ ASSERT_TRUE(android::base::WriteFully(tf->fd, ptr, ehdr_size));
+ }
+
+ void VerifyWithinSameMap(bool cache_enabled);
+ void VerifySameMap(bool cache_enabled);
+ void VerifyWithinSameMapNeverReadAtZero(bool cache_enabled);
+
+ static std::shared_ptr<Memory> memory_;
+};
+
+std::shared_ptr<Memory> ElfCacheTest::memory_;
+
+void ElfCacheTest::VerifySameMap(bool cache_enabled) {
+ if (!cache_enabled) {
+ Elf::SetCachingEnabled(false);
+ }
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ WriteElfFile(0, &tf, EM_ARM);
+ close(tf.fd);
+
+ uint64_t start = 0x1000;
+ uint64_t end = 0x20000;
+ MapInfo info1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+
+ Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf1->valid());
+ Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf2->valid());
+
+ if (cache_enabled) {
+ EXPECT_EQ(elf1, elf2);
+ } else {
+ EXPECT_NE(elf1, elf2);
+ }
+}
+
+TEST_F(ElfCacheTest, no_caching) {
+ VerifySameMap(false);
+}
+
+TEST_F(ElfCacheTest, caching_invalid_elf) {
+ VerifySameMap(true);
+}
+
+void ElfCacheTest::VerifyWithinSameMap(bool cache_enabled) {
+ if (!cache_enabled) {
+ Elf::SetCachingEnabled(false);
+ }
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ WriteElfFile(0, &tf, EM_ARM);
+ WriteElfFile(0x100, &tf, EM_386);
+ WriteElfFile(0x200, &tf, EM_X86_64);
+ lseek(tf.fd, 0x500, SEEK_SET);
+ uint8_t value = 0;
+ write(tf.fd, &value, 1);
+ close(tf.fd);
+
+ uint64_t start = 0x1000;
+ uint64_t end = 0x20000;
+ // Will have an elf at offset 0 in file.
+ MapInfo info0_1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info0_2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+ // Will have an elf at offset 0x100 in file.
+ MapInfo info100_1(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_2(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
+ // Will have an elf at offset 0x200 in file.
+ MapInfo info200_1(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_2(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
+ // Will have an elf at offset 0 in file.
+ MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+
+ Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf0_1->valid());
+ EXPECT_EQ(ARCH_ARM, elf0_1->arch());
+ Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf0_2->valid());
+ EXPECT_EQ(ARCH_ARM, elf0_2->arch());
+ EXPECT_EQ(0U, info0_1.elf_offset);
+ EXPECT_EQ(0U, info0_2.elf_offset);
+ if (cache_enabled) {
+ EXPECT_EQ(elf0_1, elf0_2);
+ } else {
+ EXPECT_NE(elf0_1, elf0_2);
+ }
+
+ Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
+ ASSERT_TRUE(elf100_1->valid());
+ EXPECT_EQ(ARCH_X86, elf100_1->arch());
+ Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
+ ASSERT_TRUE(elf100_2->valid());
+ EXPECT_EQ(ARCH_X86, elf100_2->arch());
+ EXPECT_EQ(0U, info100_1.elf_offset);
+ EXPECT_EQ(0U, info100_2.elf_offset);
+ if (cache_enabled) {
+ EXPECT_EQ(elf100_1, elf100_2);
+ } else {
+ EXPECT_NE(elf100_1, elf100_2);
+ }
+
+ Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
+ ASSERT_TRUE(elf200_1->valid());
+ EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
+ Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
+ ASSERT_TRUE(elf200_2->valid());
+ EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
+ EXPECT_EQ(0U, info200_1.elf_offset);
+ EXPECT_EQ(0U, info200_2.elf_offset);
+ if (cache_enabled) {
+ EXPECT_EQ(elf200_1, elf200_2);
+ } else {
+ EXPECT_NE(elf200_1, elf200_2);
+ }
+
+ Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf300_1->valid());
+ EXPECT_EQ(ARCH_ARM, elf300_1->arch());
+ Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf300_2->valid());
+ EXPECT_EQ(ARCH_ARM, elf300_2->arch());
+ EXPECT_EQ(0x300U, info300_1.elf_offset);
+ EXPECT_EQ(0x300U, info300_2.elf_offset);
+ if (cache_enabled) {
+ EXPECT_EQ(elf300_1, elf300_2);
+ EXPECT_EQ(elf0_1, elf300_1);
+ } else {
+ EXPECT_NE(elf300_1, elf300_2);
+ EXPECT_NE(elf0_1, elf300_1);
+ }
+}
+
+TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero) {
+ VerifyWithinSameMap(false);
+}
+
+TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero) {
+ VerifyWithinSameMap(true);
+}
+
+// Verify that when reading from multiple non-zero offsets in the same map
+// that when cached, all of the elf objects are the same.
+void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
+ if (!cache_enabled) {
+ Elf::SetCachingEnabled(false);
+ }
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ WriteElfFile(0, &tf, EM_ARM);
+ lseek(tf.fd, 0x500, SEEK_SET);
+ uint8_t value = 0;
+ write(tf.fd, &value, 1);
+ close(tf.fd);
+
+ uint64_t start = 0x1000;
+ uint64_t end = 0x20000;
+ // Multiple info sections at different offsets will have non-zero elf offsets.
+ MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info400_1(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
+ MapInfo info400_2(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
+
+ Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf300_1->valid());
+ EXPECT_EQ(ARCH_ARM, elf300_1->arch());
+ Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf300_2->valid());
+ EXPECT_EQ(ARCH_ARM, elf300_2->arch());
+ EXPECT_EQ(0x300U, info300_1.elf_offset);
+ EXPECT_EQ(0x300U, info300_2.elf_offset);
+ if (cache_enabled) {
+ EXPECT_EQ(elf300_1, elf300_2);
+ } else {
+ EXPECT_NE(elf300_1, elf300_2);
+ }
+
+ Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf400_1->valid());
+ EXPECT_EQ(ARCH_ARM, elf400_1->arch());
+ Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf400_2->valid());
+ EXPECT_EQ(ARCH_ARM, elf400_2->arch());
+ EXPECT_EQ(0x400U, info400_1.elf_offset);
+ EXPECT_EQ(0x400U, info400_2.elf_offset);
+ if (cache_enabled) {
+ EXPECT_EQ(elf400_1, elf400_2);
+ EXPECT_EQ(elf300_1, elf400_1);
+ } else {
+ EXPECT_NE(elf400_1, elf400_2);
+ EXPECT_NE(elf300_1, elf400_1);
+ }
+}
+
+TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero_never_read_at_zero) {
+ VerifyWithinSameMapNeverReadAtZero(false);
+}
+
+TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero_never_read_at_zero) {
+ VerifyWithinSameMapNeverReadAtZero(true);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
new file mode 100644
index 000000000..3d5ddd6b6
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+std::deque<FunctionData> ElfInterfaceFake::functions_;
+std::deque<StepData> ElfInterfaceFake::steps_;
+
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
+ if (functions_.empty()) {
+ return false;
+ }
+ auto entry = functions_.front();
+ functions_.pop_front();
+ *name = entry.name;
+ *offset = entry.offset;
+ return true;
+}
+
+bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) {
+ auto entry = globals_.find(global);
+ if (entry == globals_.end()) {
+ return false;
+ }
+ *offset = entry->second;
+ return true;
+}
+
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+ if (steps_.empty()) {
+ return false;
+ }
+ auto entry = steps_.front();
+ steps_.pop_front();
+
+ if (entry.pc == 0 && entry.sp == 0 && !entry.finished) {
+ // Pretend as though there is no frame.
+ return false;
+ }
+
+ RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
+ fake_regs->set_pc(entry.pc);
+ fake_regs->set_sp(entry.sp);
+ *finished = entry.finished;
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
new file mode 100644
index 000000000..fc90dab8e
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <unordered_map>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+struct StepData {
+ StepData(uint64_t pc, uint64_t sp, bool finished) : pc(pc), sp(sp), finished(finished) {}
+ uint64_t pc;
+ uint64_t sp;
+ bool finished;
+};
+
+struct FunctionData {
+ FunctionData(std::string name, uint64_t offset) : name(name), offset(offset) {}
+
+ std::string name;
+ uint64_t offset;
+};
+
+class ElfFake : public Elf {
+ public:
+ ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
+ virtual ~ElfFake() = default;
+
+ void FakeSetValid(bool valid) { valid_ = valid; }
+
+ void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+
+ void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+ void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
+ gnu_debugdata_interface_.reset(interface);
+ }
+};
+
+class ElfInterfaceFake : public ElfInterface {
+ public:
+ ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
+ virtual ~ElfInterfaceFake() = default;
+
+ bool Init(int64_t*) override { return false; }
+ void InitHeaders() override {}
+ std::string GetSoname() override { return fake_soname_; }
+
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
+ bool GetGlobalVariable(const std::string&, uint64_t*) override;
+ std::string GetBuildID() override { return fake_build_id_; }
+
+ bool Step(uint64_t, Regs*, Memory*, bool*) override;
+
+ void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
+ globals_[global] = offset;
+ }
+
+ void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
+ void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+
+ void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
+
+ static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
+ static void FakePushStepData(const StepData data) { steps_.push_back(data); }
+
+ static void FakeClear() {
+ functions_.clear();
+ steps_.clear();
+ }
+
+ void FakeSetErrorCode(ErrorCode code) { last_error_.code = code; }
+
+ void FakeSetErrorAddress(uint64_t address) { last_error_.address = address; }
+
+ void FakeSetDataOffset(uint64_t offset) { data_offset_ = offset; }
+ void FakeSetDataVaddrStart(uint64_t vaddr) { data_vaddr_start_ = vaddr; }
+ void FakeSetDataVaddrEnd(uint64_t vaddr) { data_vaddr_end_ = vaddr; }
+
+ void FakeSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+ void FakeSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
+ void FakeSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }
+
+ void FakeSetGnuDebugdataOffset(uint64_t offset) { gnu_debugdata_offset_ = offset; }
+ void FakeSetGnuDebugdataSize(uint64_t size) { gnu_debugdata_size_ = size; }
+
+ private:
+ std::unordered_map<std::string, uint64_t> globals_;
+ std::string fake_build_id_;
+ std::string fake_soname_;
+
+ static std::deque<FunctionData> functions_;
+ static std::deque<StepData> steps_;
+};
+
+class ElfInterface32Fake : public ElfInterface32 {
+ public:
+ ElfInterface32Fake(Memory* memory) : ElfInterface32(memory) {}
+ virtual ~ElfInterface32Fake() = default;
+
+ void FakeSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+ void FakeSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+ void FakeSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+ void FakeSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class ElfInterface64Fake : public ElfInterface64 {
+ public:
+ ElfInterface64Fake(Memory* memory) : ElfInterface64(memory) {}
+ virtual ~ElfInterface64Fake() = default;
+
+ void FakeSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+ void FakeSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+ void FakeSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+ void FakeSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class ElfInterfaceArmFake : public ElfInterfaceArm {
+ public:
+ ElfInterfaceArmFake(Memory* memory) : ElfInterfaceArm(memory) {}
+ virtual ~ElfInterfaceArmFake() = default;
+
+ void FakeSetStartOffset(uint64_t offset) { start_offset_ = offset; }
+ void FakeSetTotalEntries(size_t entries) { total_entries_ = entries; }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
new file mode 100644
index 000000000..43c6a9734
--- /dev/null
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/RegsArm.h>
+
+#include "ElfInterfaceArm.h"
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class ElfInterfaceArmTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ process_memory_.Clear();
+ }
+
+ MemoryFake memory_;
+ MemoryFake process_memory_;
+};
+
+TEST_F(ElfInterfaceArmTest, GetPrel32Addr) {
+ ElfInterfaceArmFake interface(&memory_);
+ memory_.SetData32(0x1000, 0x230000);
+
+ uint32_t value;
+ ASSERT_TRUE(interface.GetPrel31Addr(0x1000, &value));
+ ASSERT_EQ(0x231000U, value);
+
+ memory_.SetData32(0x1000, 0x80001000);
+ ASSERT_TRUE(interface.GetPrel31Addr(0x1000, &value));
+ ASSERT_EQ(0x2000U, value);
+
+ memory_.SetData32(0x1000, 0x70001000);
+ ASSERT_TRUE(interface.GetPrel31Addr(0x1000, &value));
+ ASSERT_EQ(0xf0002000U, value);
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_start_zero) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0);
+ interface.FakeSetTotalEntries(10);
+
+ uint64_t entry_offset;
+ ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_no_entries) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x100);
+ interface.FakeSetTotalEntries(0);
+
+ uint64_t entry_offset;
+ ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_no_valid_memory) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x100);
+ interface.FakeSetTotalEntries(2);
+
+ uint64_t entry_offset;
+ ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_ip_before_first) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(1);
+ memory_.SetData32(0x1000, 0x6000);
+
+ uint64_t entry_offset;
+ ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_single_entry_negative_value) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x8000);
+ interface.FakeSetTotalEntries(1);
+ memory_.SetData32(0x8000, 0x7fffff00);
+
+ uint64_t entry_offset;
+ ASSERT_TRUE(interface.FindEntry(0x7ff0, &entry_offset));
+ ASSERT_EQ(0x8000U, entry_offset);
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_two_entries) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(2);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1008, 0x7000);
+
+ uint64_t entry_offset;
+ ASSERT_TRUE(interface.FindEntry(0x7000, &entry_offset));
+ ASSERT_EQ(0x1000U, entry_offset);
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_last_check_single_entry) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(1);
+ memory_.SetData32(0x1000, 0x6000);
+
+ uint64_t entry_offset;
+ ASSERT_TRUE(interface.FindEntry(0x7000, &entry_offset));
+ ASSERT_EQ(0x1000U, entry_offset);
+
+ // To guarantee that we are using the cache on the second run,
+ // set the memory to a different value.
+ memory_.SetData32(0x1000, 0x8000);
+ ASSERT_TRUE(interface.FindEntry(0x7004, &entry_offset));
+ ASSERT_EQ(0x1000U, entry_offset);
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_last_check_multiple_entries) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(2);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1008, 0x8000);
+
+ uint64_t entry_offset;
+ ASSERT_TRUE(interface.FindEntry(0x9008, &entry_offset));
+ ASSERT_EQ(0x1008U, entry_offset);
+
+ // To guarantee that we are using the cache on the second run,
+ // set the memory to a different value.
+ memory_.SetData32(0x1000, 0x16000);
+ memory_.SetData32(0x1008, 0x18000);
+ ASSERT_TRUE(interface.FindEntry(0x9100, &entry_offset));
+ ASSERT_EQ(0x1008U, entry_offset);
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_even) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(4);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1008, 0x7000);
+ memory_.SetData32(0x1010, 0x8000);
+ memory_.SetData32(0x1018, 0x9000);
+
+ uint64_t entry_offset;
+ ASSERT_TRUE(interface.FindEntry(0x9100, &entry_offset));
+ ASSERT_EQ(0x1010U, entry_offset);
+
+ // To guarantee that we are using the cache on the second run,
+ // set the memory to a different value.
+ memory_.SetData32(0x1000, 0x16000);
+ memory_.SetData32(0x1008, 0x17000);
+ memory_.SetData32(0x1010, 0x18000);
+ memory_.SetData32(0x1018, 0x19000);
+ ASSERT_TRUE(interface.FindEntry(0x9100, &entry_offset));
+ ASSERT_EQ(0x1010U, entry_offset);
+}
+
+TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_odd) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(5);
+ memory_.SetData32(0x1000, 0x5000);
+ memory_.SetData32(0x1008, 0x6000);
+ memory_.SetData32(0x1010, 0x7000);
+ memory_.SetData32(0x1018, 0x8000);
+ memory_.SetData32(0x1020, 0x9000);
+
+ uint64_t entry_offset;
+ ASSERT_TRUE(interface.FindEntry(0x8100, &entry_offset));
+ ASSERT_EQ(0x1010U, entry_offset);
+
+ // To guarantee that we are using the cache on the second run,
+ // set the memory to a different value.
+ memory_.SetData32(0x1000, 0x15000);
+ memory_.SetData32(0x1008, 0x16000);
+ memory_.SetData32(0x1010, 0x17000);
+ memory_.SetData32(0x1018, 0x18000);
+ memory_.SetData32(0x1020, 0x19000);
+ ASSERT_TRUE(interface.FindEntry(0x8100, &entry_offset));
+ ASSERT_EQ(0x1010U, entry_offset);
+}
+
+TEST_F(ElfInterfaceArmTest, iterate) {
+ ElfInterfaceArmFake interface(&memory_);
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(5);
+ memory_.SetData32(0x1000, 0x5000);
+ memory_.SetData32(0x1008, 0x6000);
+ memory_.SetData32(0x1010, 0x7000);
+ memory_.SetData32(0x1018, 0x8000);
+ memory_.SetData32(0x1020, 0x9000);
+
+ std::vector<uint32_t> entries;
+ for (auto addr : interface) {
+ entries.push_back(addr);
+ }
+ ASSERT_EQ(5U, entries.size());
+ ASSERT_EQ(0x6000U, entries[0]);
+ ASSERT_EQ(0x7008U, entries[1]);
+ ASSERT_EQ(0x8010U, entries[2]);
+ ASSERT_EQ(0x9018U, entries[3]);
+ ASSERT_EQ(0xa020U, entries[4]);
+
+ // Make sure the iterate cached the entries.
+ memory_.SetData32(0x1000, 0x11000);
+ memory_.SetData32(0x1008, 0x12000);
+ memory_.SetData32(0x1010, 0x13000);
+ memory_.SetData32(0x1018, 0x14000);
+ memory_.SetData32(0x1020, 0x15000);
+
+ entries.clear();
+ for (auto addr : interface) {
+ entries.push_back(addr);
+ }
+ ASSERT_EQ(5U, entries.size());
+ ASSERT_EQ(0x6000U, entries[0]);
+ ASSERT_EQ(0x7008U, entries[1]);
+ ASSERT_EQ(0x8010U, entries[2]);
+ ASSERT_EQ(0x9018U, entries[3]);
+ ASSERT_EQ(0xa020U, entries[4]);
+}
+
+TEST_F(ElfInterfaceArmTest, HandleUnknownType_arm_exidx) {
+ ElfInterfaceArmFake interface(&memory_);
+
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(100);
+
+ // Verify that if the type is not the one we want, we don't set the values.
+ interface.HandleUnknownType(0x70000000, 0x2000, 320);
+ ASSERT_EQ(0x1000U, interface.start_offset());
+ ASSERT_EQ(100U, interface.total_entries());
+
+ // Everything is correct and present.
+ interface.HandleUnknownType(0x70000001, 0x2000, 320);
+ ASSERT_EQ(0x2000U, interface.start_offset());
+ ASSERT_EQ(40U, interface.total_entries());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx) {
+ ElfInterfaceArmFake interface(&memory_);
+
+ // FindEntry fails.
+ bool finished;
+ ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
+ EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+
+ // ExtractEntry should fail.
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(2);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1008, 0x8000);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x1000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+ ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
+ EXPECT_EQ(0x1004U, interface.LastErrorAddress());
+
+ // Eval should fail.
+ memory_.SetData32(0x1004, 0x81000000);
+ ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+
+ // Everything should pass.
+ memory_.SetData32(0x1004, 0x80b0b0b0);
+ ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+ ASSERT_FALSE(finished);
+ ASSERT_EQ(0x1000U, regs.sp());
+ ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
+ ASSERT_EQ(0x20000U, regs.pc());
+ ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
+
+ // Load bias is non-zero.
+ interface.set_load_bias(0x1000);
+ ASSERT_TRUE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+
+ // Pc too small.
+ interface.set_load_bias(0x9000);
+ ASSERT_FALSE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
+ ElfInterfaceArmFake interface(&memory_);
+
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(2);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1004, 0x808800b0);
+ memory_.SetData32(0x1008, 0x8000);
+ process_memory_.SetData32(0x10000, 0x10);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ // Everything should pass.
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+ ASSERT_FALSE(finished);
+ ASSERT_EQ(0x10004U, regs.sp());
+ ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
+ ASSERT_EQ(0x10U, regs.pc());
+ ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
+ ElfInterfaceArmFake interface(&memory_);
+
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(1);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1004, 1);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+ ASSERT_TRUE(finished);
+ ASSERT_EQ(0x10000U, regs.sp());
+ ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+ ASSERT_EQ(0x1234U, regs.pc());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
+ ElfInterfaceArmFake interface(&memory_);
+
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(1);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1004, 0x808000b0);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+ ASSERT_TRUE(finished);
+ ASSERT_EQ(0x10000U, regs.sp());
+ ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+ ASSERT_EQ(0x1234U, regs.pc());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) {
+ ElfInterfaceArmFake interface(&memory_);
+
+ interface.FakeSetStartOffset(0x1000);
+ interface.FakeSetTotalEntries(1);
+ memory_.SetData32(0x1000, 0x6000);
+ // Set the pc using a pop r15 command.
+ memory_.SetData32(0x1004, 0x808800b0);
+
+ // pc value of zero.
+ process_memory_.SetData32(0x10000, 0);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+ ASSERT_TRUE(finished);
+ ASSERT_EQ(0U, regs.pc());
+
+ // Now set the pc from the lr register (pop r14).
+ memory_.SetData32(0x1004, 0x808400b0);
+
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+ EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+ ASSERT_TRUE(finished);
+ ASSERT_EQ(0U, regs.pc());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
new file mode 100644
index 000000000..3cf90fe4c
--- /dev/null
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -0,0 +1,1966 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/ElfInterface.h>
+
+#include "DwarfEncoding.h"
+#include "ElfInterfaceArm.h"
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+#if !defined(PT_ARM_EXIDX)
+#define PT_ARM_EXIDX 0x70000001
+#endif
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+namespace unwindstack {
+
+class ElfInterfaceTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ }
+
+ void SetStringMemory(uint64_t offset, const char* string) {
+ memory_.SetMemory(offset, string, strlen(string) + 1);
+ }
+
+ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+ void SinglePtLoad();
+
+ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+ void MultipleExecutablePtLoads();
+
+ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+ void MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr();
+
+ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+ void NonExecutablePtLoads();
+
+ template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+ void ManyPhdrs();
+
+ enum SonameTestEnum : uint8_t {
+ SONAME_NORMAL,
+ SONAME_DTNULL_AFTER,
+ SONAME_DTSIZE_SMALL,
+ SONAME_MISSING_MAP,
+ };
+
+ template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+ void SonameInit(SonameTestEnum test_type = SONAME_NORMAL);
+
+ template <typename ElfInterfaceType>
+ void Soname();
+
+ template <typename ElfInterfaceType>
+ void SonameAfterDtNull();
+
+ template <typename ElfInterfaceType>
+ void SonameSize();
+
+ template <typename ElfInterfaceType>
+ void SonameMissingMap();
+
+ template <typename ElfType>
+ void InitHeadersEhFrameTest();
+
+ template <typename ElfType>
+ void InitHeadersDebugFrame();
+
+ template <typename ElfType>
+ void InitHeadersEhFrameFail();
+
+ template <typename ElfType>
+ void InitHeadersDebugFrameFail();
+
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void InitProgramHeadersMalformed();
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersMalformed();
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersMalformedSymData();
+
+ template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+ void InitSectionHeaders(uint64_t entry_size);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsets();
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsetsEhFrameSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsetsEhFrameHdrSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias);
+
+ template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+ void InitSectionHeadersOffsetsDebugFrameSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias);
+
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void CheckGnuEhFrame(uint64_t addr, uint64_t offset, int64_t expected_bias);
+
+ template <typename Sym>
+ void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name);
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildID();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDTwoNotes();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDSectionTooSmallForName();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDSectionTooSmallForDesc();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDSectionTooSmallForHeader();
+
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void CheckLoadBiasInFirstPhdr(int64_t load_bias);
+
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr, int64_t load_bias);
+
+ MemoryFake memory_;
+};
+
+template <typename Sym>
+void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
+ uint64_t sym_offset, const char* name) {
+ Sym sym = {};
+ sym.st_info = STT_FUNC;
+ sym.st_value = value;
+ sym.st_size = size;
+ sym.st_name = name_offset;
+ sym.st_shndx = SHN_COMMON;
+
+ memory_.SetMemory(offset, &sym, sizeof(sym));
+ memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1);
+}
+
+template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+void ElfInterfaceTest::SinglePtLoad() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000, load_bias);
+
+ const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
+ ASSERT_EQ(1U, pt_loads.size());
+ LoadInfo load_data = pt_loads.at(0);
+ ASSERT_EQ(0U, load_data.offset);
+ ASSERT_EQ(0x2000U, load_data.table_offset);
+ ASSERT_EQ(0x10000U, load_data.table_size);
+}
+
+TEST_F(ElfInterfaceTest, single_pt_load_32) {
+ SinglePtLoad<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, single_pt_load_64) {
+ SinglePtLoad<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+void ElfInterfaceTest::MultipleExecutablePtLoads() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1000;
+ phdr.p_vaddr = 0x2001;
+ phdr.p_memsz = 0x10001;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1001;
+ memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x2000;
+ phdr.p_vaddr = 0x2002;
+ phdr.p_memsz = 0x10002;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1002;
+ memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000, load_bias);
+
+ const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
+ ASSERT_EQ(3U, pt_loads.size());
+
+ LoadInfo load_data = pt_loads.at(0);
+ ASSERT_EQ(0U, load_data.offset);
+ ASSERT_EQ(0x2000U, load_data.table_offset);
+ ASSERT_EQ(0x10000U, load_data.table_size);
+
+ load_data = pt_loads.at(0x1000);
+ ASSERT_EQ(0x1000U, load_data.offset);
+ ASSERT_EQ(0x2001U, load_data.table_offset);
+ ASSERT_EQ(0x10001U, load_data.table_size);
+
+ load_data = pt_loads.at(0x2000);
+ ASSERT_EQ(0x2000U, load_data.offset);
+ ASSERT_EQ(0x2002U, load_data.table_offset);
+ ASSERT_EQ(0x10002U, load_data.table_size);
+}
+
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_32) {
+ MultipleExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_64) {
+ MultipleExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr) + 100;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1000;
+ phdr.p_vaddr = 0x2001;
+ phdr.p_memsz = 0x10001;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1001;
+ memory_.SetMemory(0x100 + sizeof(phdr) + 100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x2000;
+ phdr.p_vaddr = 0x2002;
+ phdr.p_memsz = 0x10002;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1002;
+ memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000, load_bias);
+
+ const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
+ ASSERT_EQ(3U, pt_loads.size());
+
+ LoadInfo load_data = pt_loads.at(0);
+ ASSERT_EQ(0U, load_data.offset);
+ ASSERT_EQ(0x2000U, load_data.table_offset);
+ ASSERT_EQ(0x10000U, load_data.table_size);
+
+ load_data = pt_loads.at(0x1000);
+ ASSERT_EQ(0x1000U, load_data.offset);
+ ASSERT_EQ(0x2001U, load_data.table_offset);
+ ASSERT_EQ(0x10001U, load_data.table_size);
+
+ load_data = pt_loads.at(0x2000);
+ ASSERT_EQ(0x2000U, load_data.offset);
+ ASSERT_EQ(0x2002U, load_data.table_offset);
+ ASSERT_EQ(0x10002U, load_data.table_size);
+}
+
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_32) {
+ MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn,
+ ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_64) {
+ MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn,
+ ElfInterface64>();
+}
+
+template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+void ElfInterfaceTest::NonExecutablePtLoads() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1000;
+ phdr.p_vaddr = 0x2001;
+ phdr.p_memsz = 0x10001;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1001;
+ memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x2000;
+ phdr.p_vaddr = 0x2002;
+ phdr.p_memsz = 0x10002;
+ phdr.p_flags = PF_R;
+ phdr.p_align = 0x1002;
+ memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x1001, load_bias);
+
+ const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
+ ASSERT_EQ(1U, pt_loads.size());
+
+ LoadInfo load_data = pt_loads.at(0x1000);
+ ASSERT_EQ(0x1000U, load_data.offset);
+ ASSERT_EQ(0x2001U, load_data.table_offset);
+ ASSERT_EQ(0x10001U, load_data.table_size);
+}
+
+TEST_F(ElfInterfaceTest, non_executable_pt_loads_32) {
+ NonExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, non_executable_pt_loads_64) {
+ NonExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+void ElfInterfaceTest::ManyPhdrs() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 7;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ uint64_t phdr_offset = 0x100;
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_GNU_EH_FRAME;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_DYNAMIC;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_INTERP;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_NOTE;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_SHLIB;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_GNU_EH_FRAME;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000, load_bias);
+
+ const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
+ ASSERT_EQ(1U, pt_loads.size());
+
+ LoadInfo load_data = pt_loads.at(0);
+ ASSERT_EQ(0U, load_data.offset);
+ ASSERT_EQ(0x2000U, load_data.table_offset);
+ ASSERT_EQ(0x10000U, load_data.table_size);
+}
+
+TEST_F(ElfInterfaceTest, many_phdrs_32) {
+ ElfInterfaceTest::ManyPhdrs<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, many_phdrs_64) {
+ ElfInterfaceTest::ManyPhdrs<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, arm32) {
+ ElfInterfaceArm elf_arm(&memory_);
+
+ Elf32_Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr = {};
+ phdr.p_type = PT_ARM_EXIDX;
+ phdr.p_offset = 0x2000;
+ phdr.p_filesz = 16;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ // Add arm exidx entries.
+ memory_.SetData32(0x2000, 0x1000);
+ memory_.SetData32(0x2008, 0x1000);
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf_arm.Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+
+ std::vector<uint32_t> entries;
+ for (auto addr : elf_arm) {
+ entries.push_back(addr);
+ }
+ ASSERT_EQ(2U, entries.size());
+ ASSERT_EQ(0x3000U, entries[0]);
+ ASSERT_EQ(0x3008U, entries[1]);
+
+ ASSERT_EQ(0x2000U, elf_arm.start_offset());
+ ASSERT_EQ(2U, elf_arm.total_entries());
+}
+
+template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
+ Ehdr ehdr = {};
+ ehdr.e_shoff = 0x200;
+ ehdr.e_shnum = 2;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_STRTAB;
+ if (test_type == SONAME_MISSING_MAP) {
+ shdr.sh_addr = 0x20100;
+ } else {
+ shdr.sh_addr = 0x10100;
+ }
+ shdr.sh_offset = 0x10000;
+ memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_DYNAMIC;
+ phdr.p_offset = 0x2000;
+ phdr.p_memsz = sizeof(Dyn) * 3;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ uint64_t offset = 0x2000;
+ Dyn dyn;
+
+ dyn.d_tag = DT_STRTAB;
+ dyn.d_un.d_ptr = 0x10100;
+ memory_.SetMemory(offset, &dyn, sizeof(dyn));
+ offset += sizeof(dyn);
+
+ dyn.d_tag = DT_STRSZ;
+ if (test_type == SONAME_DTSIZE_SMALL) {
+ dyn.d_un.d_val = 0x10;
+ } else {
+ dyn.d_un.d_val = 0x1000;
+ }
+ memory_.SetMemory(offset, &dyn, sizeof(dyn));
+ offset += sizeof(dyn);
+
+ if (test_type == SONAME_DTNULL_AFTER) {
+ dyn.d_tag = DT_NULL;
+ memory_.SetMemory(offset, &dyn, sizeof(dyn));
+ offset += sizeof(dyn);
+ }
+
+ dyn.d_tag = DT_SONAME;
+ dyn.d_un.d_val = 0x10;
+ memory_.SetMemory(offset, &dyn, sizeof(dyn));
+ offset += sizeof(dyn);
+
+ dyn.d_tag = DT_NULL;
+ memory_.SetMemory(offset, &dyn, sizeof(dyn));
+
+ SetStringMemory(0x10010, "fake_soname.so");
+}
+
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::Soname() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+
+ ASSERT_EQ("fake_soname.so", elf->GetSoname());
+}
+
+TEST_F(ElfInterfaceTest, soname_32) {
+ SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
+ Soname<ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, soname_64) {
+ SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
+ Soname<ElfInterface64>();
+}
+
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::SonameAfterDtNull() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+
+ ASSERT_EQ("", elf->GetSoname());
+}
+
+TEST_F(ElfInterfaceTest, soname_after_dt_null_32) {
+ SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
+ SonameAfterDtNull<ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, soname_after_dt_null_64) {
+ SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
+ SonameAfterDtNull<ElfInterface64>();
+}
+
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::SonameSize() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+
+ ASSERT_EQ("", elf->GetSoname());
+}
+
+TEST_F(ElfInterfaceTest, soname_size_32) {
+ SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
+ SonameSize<ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, soname_size_64) {
+ SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
+ SonameSize<ElfInterface64>();
+}
+
+// Verify that there is no map from STRTAB in the dynamic section to a
+// STRTAB entry in the section headers.
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::SonameMissingMap() {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+
+ ASSERT_EQ("", elf->GetSoname());
+}
+
+TEST_F(ElfInterfaceTest, soname_missing_map_32) {
+ SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
+ SonameMissingMap<ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, soname_missing_map_64) {
+ SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
+ SonameMissingMap<ElfInterface64>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersEhFrameTest() {
+ ElfType elf(&memory_);
+
+ elf.FakeSetEhFrameOffset(0x10000);
+ elf.FakeSetEhFrameSize(0);
+ elf.FakeSetDebugFrameOffset(0);
+ elf.FakeSetDebugFrameSize(0);
+
+ memory_.SetMemory(0x10000,
+ std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
+ memory_.SetData32(0x10004, 0x500);
+ memory_.SetData32(0x10008, 250);
+
+ elf.InitHeaders();
+
+ EXPECT_FALSE(elf.eh_frame() == nullptr);
+ EXPECT_TRUE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame_32) {
+ InitHeadersEhFrameTest<ElfInterface32Fake>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_eh_frame_64) {
+ InitHeadersEhFrameTest<ElfInterface64Fake>();
+}
+
+template <typename ElfType>
+void ElfInterfaceTest::InitHeadersDebugFrame() {
+ ElfType elf(&memory_);
+
+ elf.FakeSetEhFrameOffset(0);
+ elf.FakeSetEhFrameSize(0);
+ elf.FakeSetDebugFrameOffset(0x5000);
+ elf.FakeSetDebugFrameSize(0x200);
+
+ memory_.SetData32(0x5000, 0xfc);
+ memory_.SetData32(0x5004, 0xffffffff);
+ memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 4, 8, 2});
+
+ memory_.SetData32(0x5100, 0xfc);
+ memory_.SetData32(0x5104, 0);
+ memory_.SetData32(0x5108, 0x1500);
+ memory_.SetData32(0x510c, 0x200);
+
+ elf.InitHeaders();
+
+ EXPECT_TRUE(elf.eh_frame() == nullptr);
+ EXPECT_FALSE(elf.debug_frame() == nullptr);
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame_32) {
+ InitHeadersDebugFrame<ElfInterface32Fake>();
+}
+
+TEST_F(ElfInterfaceTest, init_headers_debug_frame_64) {
+ InitHeadersDebugFrame<ElfInterface64Fake>();
+}
+
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitProgramHeadersMalformed() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+}
+
+TEST_F(ElfInterfaceTest, init_program_headers_malformed_32) {
+ InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_program_headers_malformed_64) {
+ InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformed() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = 0x1000;
+ ehdr.e_shnum = 10;
+ ehdr.e_shentsize = sizeof(Shdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_32) {
+ InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_64) {
+ InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersMalformedSymData() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x1000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 5;
+ ehdr.e_shentsize = sizeof(Shdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 10;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 2;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for the entries.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(0U, elf->debug_frame_offset());
+ EXPECT_EQ(0U, elf->debug_frame_size());
+ EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+ std::string name;
+ uint64_t name_offset;
+ ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_32) {
+ InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_64) {
+ InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x1000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 5;
+ ehdr.e_shentsize = entry_size;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_DYNSYM;
+ shdr.sh_link = 4;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = sizeof(Sym);
+ shdr.sh_size = shdr.sh_entsize * 10;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0xa000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for the entries.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
+ InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(0U, elf->debug_frame_offset());
+ EXPECT_EQ(0U, elf->debug_frame_size());
+ EXPECT_EQ(0U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0U, elf->gnu_debugdata_size());
+
+ // Look in the first symbol table.
+ std::string name;
+ uint64_t name_offset;
+ ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset));
+ EXPECT_EQ("function_one", name);
+ EXPECT_EQ(16U, name_offset);
+ ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset));
+ EXPECT_EQ("function_two", name);
+ EXPECT_EQ(32U, name_offset);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_32) {
+ InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_64) {
+ InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsets() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 7;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x6000;
+ shdr.sh_offset = 0x6000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x300;
+ shdr.sh_addr = 0x7000;
+ shdr.sh_offset = 0x7000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x400;
+ shdr.sh_addr = 0xa000;
+ shdr.sh_offset = 0xa000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0xf00;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_addr = 0xb000;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = 0xf00;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+ memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+ memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame"));
+ memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(0x6000U, elf->debug_frame_offset());
+ EXPECT_EQ(0, elf->debug_frame_section_bias());
+ EXPECT_EQ(0x500U, elf->debug_frame_size());
+
+ EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
+ EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+
+ EXPECT_EQ(0x7000U, elf->eh_frame_offset());
+ EXPECT_EQ(0, elf->eh_frame_section_bias());
+ EXPECT_EQ(0x800U, elf->eh_frame_size());
+
+ EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
+ EXPECT_EQ(0, elf->eh_frame_hdr_section_bias());
+ EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
+
+ EXPECT_EQ(0xb000U, elf->gnu_build_id_offset());
+ EXPECT_EQ(0xf00U, elf->gnu_build_id_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_32) {
+ InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_64) {
+ InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsetsEhFrameSectionBias(uint64_t addr, uint64_t offset,
+ int64_t expected_bias) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t elf_offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = elf_offset;
+ ehdr.e_shnum = 4;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ elf_offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = 0x8000;
+ shdr.sh_offset = 0x8000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = addr;
+ shdr.sh_offset = offset;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".eh_frame", sizeof(".eh_frame"));
+ memory_.SetMemory(0xf200, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(offset, elf->eh_frame_offset());
+ EXPECT_EQ(expected_bias, elf->eh_frame_section_bias());
+ EXPECT_EQ(0x500U, elf->eh_frame_size());
+
+ EXPECT_EQ(0x8000U, elf->eh_frame_hdr_offset());
+ EXPECT_EQ(0, elf->eh_frame_hdr_section_bias());
+ EXPECT_EQ(0x800U, elf->eh_frame_hdr_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_zero_32) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x4000,
+ 0x4000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_zero_64) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0x6000,
+ 0x6000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_positive_32) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x5000, 0x4000, 0x1000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_positive_64) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x6000, 0x4000, 0x2000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_negative_32) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x3000, 0x4000, -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_negative_64) {
+ InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x6000, 0x9000, -0x3000);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsetsEhFrameHdrSectionBias(uint64_t addr,
+ uint64_t offset,
+ int64_t expected_bias) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t elf_offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = elf_offset;
+ ehdr.e_shnum = 4;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ elf_offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x200;
+ shdr.sh_addr = addr;
+ shdr.sh_offset = offset;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x500;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".eh_frame", sizeof(".eh_frame"));
+ memory_.SetMemory(0xf200, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(0x5000U, elf->eh_frame_offset());
+ EXPECT_EQ(0, elf->eh_frame_section_bias());
+ EXPECT_EQ(0x500U, elf->eh_frame_size());
+ EXPECT_EQ(offset, elf->eh_frame_hdr_offset());
+ EXPECT_EQ(expected_bias, elf->eh_frame_hdr_section_bias());
+ EXPECT_EQ(0x800U, elf->eh_frame_hdr_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_zero_32) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x9000,
+ 0x9000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_zero_64) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0xa000,
+ 0xa000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_positive_32) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x9000, 0x4000, 0x5000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_positive_64) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x6000, 0x1000, 0x5000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_negative_32) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x3000, 0x5000, -0x2000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_negative_64) {
+ InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x5000, 0x9000, -0x4000);
+}
+
+template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
+void ElfInterfaceTest::InitSectionHeadersOffsetsDebugFrameSectionBias(uint64_t addr,
+ uint64_t offset,
+ int64_t expected_bias) {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t elf_offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = elf_offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ elf_offset += ehdr.e_shentsize;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_link = 2;
+ shdr.sh_name = 0x100;
+ shdr.sh_addr = addr;
+ shdr.sh_offset = offset;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+ elf_offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(offset, elf->debug_frame_offset());
+ EXPECT_EQ(expected_bias, elf->debug_frame_section_bias());
+ EXPECT_EQ(0x800U, elf->debug_frame_size());
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_zero_32) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x5000,
+ 0x5000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_zero_64) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0xa000,
+ 0xa000, 0);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_positive_32) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x5000, 0x2000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_positive_64) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x7000, 0x1000, 0x6000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_negative_32) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
+ 0x6000, 0x7000, -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_negative_64) {
+ InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
+ 0x3000, 0x5000, -0x2000);
+}
+
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::CheckGnuEhFrame(uint64_t addr, uint64_t offset, int64_t expected_bias) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 2;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ uint64_t phdr_offset = 0x100;
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+ phdr_offset += sizeof(phdr);
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_GNU_EH_FRAME;
+ phdr.p_vaddr = addr;
+ phdr.p_offset = offset;
+ memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_EQ(expected_bias, elf->eh_frame_hdr_section_bias());
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_zero_section_bias_32) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x4000, 0);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_zero_section_bias_64) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x4000, 0);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_positive_section_bias_32) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x1000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_positive_section_bias_64) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x1000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_negative_section_bias_32) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x5000,
+ -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, eh_frame_negative_section_bias_64) {
+ ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x5000,
+ -0x1000);
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ Elf32_Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0, load_bias);
+ EXPECT_TRUE(elf->IsValidPc(0));
+ EXPECT_TRUE(elf->IsValidPc(0x5000));
+ EXPECT_TRUE(elf->IsValidPc(0xffff));
+ EXPECT_FALSE(elf->IsValidPc(0x10000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ Elf32_Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x1000));
+ EXPECT_FALSE(elf->IsValidPc(0x1fff));
+ EXPECT_TRUE(elf->IsValidPc(0x2000));
+ EXPECT_TRUE(elf->IsValidPc(0x5000));
+ EXPECT_TRUE(elf->IsValidPc(0x11fff));
+ EXPECT_FALSE(elf->IsValidPc(0x12000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ uint64_t sh_offset = 0x100;
+
+ Elf32_Ehdr ehdr = {};
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NULL;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+ memory_.SetMemory(0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ // CIE 32.
+ memory_.SetData32(0x600, 0xfc);
+ memory_.SetData32(0x604, 0xffffffff);
+ memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
+
+ // FDE 32.
+ memory_.SetData32(0x700, 0xfc);
+ memory_.SetData32(0x704, 0);
+ memory_.SetData32(0x708, 0x2100);
+ memory_.SetData32(0x70c, 0x200);
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ elf->InitHeaders();
+ EXPECT_EQ(0, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x20ff));
+ EXPECT_TRUE(elf->IsValidPc(0x2100));
+ EXPECT_TRUE(elf->IsValidPc(0x2200));
+ EXPECT_TRUE(elf->IsValidPc(0x22ff));
+ EXPECT_FALSE(elf->IsValidPc(0x2300));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ uint64_t sh_offset = 0x100;
+
+ Elf32_Ehdr ehdr = {};
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NULL;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+ memory_.SetMemory(0x500, ".eh_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ // CIE 32.
+ memory_.SetData32(0x600, 0xfc);
+ memory_.SetData32(0x604, 0);
+ memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
+
+ // FDE 32.
+ memory_.SetData32(0x700, 0xfc);
+ memory_.SetData32(0x704, 0x104);
+ memory_.SetData32(0x708, 0x20f8);
+ memory_.SetData32(0x70c, 0x200);
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ elf->InitHeaders();
+ EXPECT_EQ(0, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x27ff));
+ EXPECT_TRUE(elf->IsValidPc(0x2800));
+ EXPECT_TRUE(elf->IsValidPc(0x2900));
+ EXPECT_TRUE(elf->IsValidPc(0x29ff));
+ EXPECT_FALSE(elf->IsValidPc(0x2a00));
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildID() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section, &note_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(&note_section[note_offset], "BUILDID", 7);
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_section);
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("BUILDID", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id_32) {
+ BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_64) {
+ BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDTwoNotes() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 8; // "WRONG" aligned to 4
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section, &note_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ memcpy(&note_section[note_offset], "WRONG", sizeof("WRONG"));
+ note_offset += 8;
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(&note_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section[note_offset], &note_header, sizeof(note_header));
+ note_offset += sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(&note_section[note_offset], "BUILDID", 7);
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_section);
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("BUILDID", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes_32) {
+ BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes_64) {
+ BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForName () {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section, &note_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(&note_section[note_offset], "BUILDID", 7);
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_header) + 1;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name_32) {
+ BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name_64) {
+ BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section, &note_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(&note_section[note_offset], "BUILDID", 7);
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_header) + sizeof("GNU") + 1;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc_32) {
+ BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc_64) {
+ BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section, &note_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(&note_section[note_offset], "BUILDID", 7);
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_header) - 1;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ int64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_32) {
+ BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_64) {
+ BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::CheckLoadBiasInFirstPhdr(int64_t load_bias) {
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 2;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0;
+ phdr.p_vaddr = load_bias;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1000;
+ phdr.p_memsz = 0x2000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ int64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
+ ASSERT_EQ(load_bias, static_load_bias);
+
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+ int64_t init_load_bias = 0;
+ ASSERT_TRUE(elf->Init(&init_load_bias));
+ ASSERT_EQ(init_load_bias, static_load_bias);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_zero_32) {
+ CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_zero_64) {
+ CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_non_zero_32) {
+ CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_non_zero_64) {
+ CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000);
+}
+
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr,
+ int64_t load_bias) {
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = vaddr;
+ phdr.p_memsz = 0x2000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ // Second executable load should be ignored for load bias computation.
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1234;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x2000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x200 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ int64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
+ ASSERT_EQ(load_bias, static_load_bias);
+
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+ int64_t init_load_bias = 0;
+ ASSERT_TRUE(elf->Init(&init_load_bias));
+ ASSERT_EQ(init_load_bias, static_load_bias);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x1000, 0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x1000, 0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_positive_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x4000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_positive_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x4000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x5000, 0x1000, -0x4000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, -0x4000);
+}
+
+TEST_F(ElfInterfaceTest, huge_gnu_debugdata_size) {
+ ElfInterfaceFake interface(nullptr);
+
+ interface.FakeSetGnuDebugdataOffset(0x1000);
+ interface.FakeSetGnuDebugdataSize(0xffffffffffffffffUL);
+ ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+
+ interface.FakeSetGnuDebugdataSize(0x4000000000000UL);
+ ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+
+ // This should exceed the size_t value of the first allocation.
+#if defined(__LP64__)
+ interface.FakeSetGnuDebugdataSize(0x3333333333333334ULL);
+#else
+ interface.FakeSetGnuDebugdataSize(0x33333334);
+#endif
+ ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
new file mode 100644
index 000000000..1f3ed8190
--- /dev/null
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+#if !defined(PT_ARM_EXIDX)
+#define PT_ARM_EXIDX 0x70000001
+#endif
+
+namespace unwindstack {
+
+class ElfTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ }
+
+ void InitElf32(uint32_t machine_type) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type);
+
+ ehdr.e_phoff = 0x100;
+ ehdr.e_ehsize = sizeof(ehdr);
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ ehdr.e_phnum = 1;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ if (machine_type == EM_ARM) {
+ ehdr.e_flags = 0x5000200;
+ ehdr.e_phnum = 2;
+ }
+ memory_->SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_filesz = 0x10000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_->SetMemory(0x100, &phdr, sizeof(phdr));
+
+ if (machine_type == EM_ARM) {
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_ARM_EXIDX;
+ phdr.p_offset = 0x30000;
+ phdr.p_vaddr = 0x30000;
+ phdr.p_paddr = 0x30000;
+ phdr.p_filesz = 16;
+ phdr.p_memsz = 16;
+ phdr.p_flags = PF_R;
+ phdr.p_align = 0x4;
+ memory_->SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+ }
+ }
+
+ void InitElf64(uint32_t machine_type) {
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type);
+
+ ehdr.e_phoff = 0x100;
+ ehdr.e_flags = 0x5000200;
+ ehdr.e_ehsize = sizeof(ehdr);
+ ehdr.e_phentsize = sizeof(Elf64_Phdr);
+ ehdr.e_phnum = 1;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr);
+ memory_->SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf64_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_filesz = 0x10000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_->SetMemory(0x100, &phdr, sizeof(phdr));
+ }
+
+ void VerifyStepIfSignalHandler(uint64_t load_bias);
+
+ MemoryFake* memory_;
+};
+
+TEST_F(ElfTest, invalid_memory) {
+ Elf elf(memory_);
+
+ ASSERT_FALSE(elf.Init());
+ ASSERT_FALSE(elf.valid());
+}
+
+TEST_F(ElfTest, elf_invalid) {
+ Elf elf(memory_);
+
+ InitElf32(EM_386);
+
+ // Corrupt the ELF signature.
+ memory_->SetData32(0, 0x7f000000);
+
+ ASSERT_FALSE(elf.Init());
+ ASSERT_FALSE(elf.valid());
+ ASSERT_TRUE(elf.interface() == nullptr);
+
+ ASSERT_EQ("", elf.GetSoname());
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
+
+ ASSERT_FALSE(elf.StepIfSignalHandler(0, nullptr, nullptr));
+ EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
+
+ bool finished;
+ ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
+ EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
+}
+
+TEST_F(ElfTest, elf32_invalid_machine) {
+ Elf elf(memory_);
+
+ InitElf32(EM_PPC);
+
+ ResetLogs();
+ ASSERT_FALSE(elf.Init());
+
+ ASSERT_EQ("", GetFakeLogBuf());
+ ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
+ GetFakeLogPrint());
+}
+
+TEST_F(ElfTest, elf64_invalid_machine) {
+ Elf elf(memory_);
+
+ InitElf64(EM_PPC64);
+
+ ResetLogs();
+ ASSERT_FALSE(elf.Init());
+
+ ASSERT_EQ("", GetFakeLogBuf());
+ ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
+ GetFakeLogPrint());
+}
+
+TEST_F(ElfTest, elf_arm) {
+ Elf elf(memory_);
+
+ InitElf32(EM_ARM);
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
+ ASSERT_EQ(ELFCLASS32, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
+TEST_F(ElfTest, elf_mips) {
+ Elf elf(memory_);
+
+ InitElf32(EM_MIPS);
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+ ASSERT_EQ(ELFCLASS32, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
+TEST_F(ElfTest, elf_x86) {
+ Elf elf(memory_);
+
+ InitElf32(EM_386);
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
+ ASSERT_EQ(ELFCLASS32, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
+TEST_F(ElfTest, elf_arm64) {
+ Elf elf(memory_);
+
+ InitElf64(EM_AARCH64);
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
+ ASSERT_EQ(ELFCLASS64, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
+TEST_F(ElfTest, elf_x86_64) {
+ Elf elf(memory_);
+
+ InitElf64(EM_X86_64);
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
+ ASSERT_EQ(ELFCLASS64, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
+TEST_F(ElfTest, elf_mips64) {
+ Elf elf(memory_);
+
+ InitElf64(EM_MIPS);
+
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+ ASSERT_EQ(ELFCLASS64, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, rel_pc) {
+ ElfFake elf(memory_);
+
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+ elf.FakeSetInterface(interface);
+
+ elf.FakeSetValid(true);
+ MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
+
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+
+ elf.FakeSetValid(false);
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+}
+
+void ElfTest::VerifyStepIfSignalHandler(uint64_t load_bias) {
+ ElfFake elf(memory_);
+
+ RegsArm regs;
+ regs[13] = 0x50000;
+ regs[15] = 0x8000;
+
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+ elf.FakeSetInterface(interface);
+ elf.FakeSetLoadBias(load_bias);
+
+ memory_->SetData32(0x3000, 0xdf0027ad);
+ MemoryFake process_memory;
+ process_memory.SetData32(0x50000, 0);
+ for (size_t i = 0; i < 16; i++) {
+ process_memory.SetData32(0x500a0 + i * sizeof(uint32_t), i);
+ }
+
+ elf.FakeSetValid(true);
+ ASSERT_TRUE(elf.StepIfSignalHandler(0x3000 + load_bias, &regs, &process_memory));
+ EXPECT_EQ(ERROR_NONE, elf.GetLastErrorCode());
+ EXPECT_EQ(15U, regs.pc());
+ EXPECT_EQ(13U, regs.sp());
+}
+
+TEST_F(ElfTest, step_in_signal_map) {
+ VerifyStepIfSignalHandler(0);
+}
+
+TEST_F(ElfTest, step_in_signal_map_non_zero_load_bias) {
+ VerifyStepIfSignalHandler(0x1000);
+}
+
+class ElfInterfaceMock : public ElfInterface {
+ public:
+ ElfInterfaceMock(Memory* memory) : ElfInterface(memory) {}
+ virtual ~ElfInterfaceMock() = default;
+
+ bool Init(int64_t*) override { return false; }
+ void InitHeaders() override {}
+ std::string GetSoname() override { return ""; }
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
+ std::string GetBuildID() override { return ""; }
+
+ MOCK_METHOD(bool, Step, (uint64_t, Regs*, Memory*, bool*), (override));
+ MOCK_METHOD(bool, GetGlobalVariable, (const std::string&, uint64_t*), (override));
+ MOCK_METHOD(bool, IsValidPc, (uint64_t), (override));
+
+ void MockSetDataOffset(uint64_t offset) { data_offset_ = offset; }
+ void MockSetDataVaddrStart(uint64_t vaddr) { data_vaddr_start_ = vaddr; }
+ void MockSetDataVaddrEnd(uint64_t vaddr) { data_vaddr_end_ = vaddr; }
+
+ void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+ void MockSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
+ void MockSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }
+};
+
+TEST_F(ElfTest, step_in_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ RegsArm regs;
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ MemoryFake process_memory;
+
+ bool finished;
+ EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
+ .WillOnce(::testing::Return(true));
+
+ ASSERT_TRUE(elf.Step(0x1000, &regs, &process_memory, &finished));
+}
+
+TEST_F(ElfTest, get_global_invalid_elf) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+
+ std::string global("something");
+ uint64_t offset;
+ ASSERT_FALSE(elf.GetGlobalVariableOffset(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_not_in_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::Return(false));
+
+ uint64_t offset;
+ ASSERT_FALSE(elf.GetGlobalVariableOffset(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_vaddr_in_no_sections) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_FALSE(elf.GetGlobalVariableOffset(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_vaddr_in_data_section) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x500);
+ interface->MockSetDataVaddrEnd(0x600);
+ interface->MockSetDataOffset(0xa000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x580), ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0xa080U, offset);
+}
+
+TEST_F(ElfTest, get_global_vaddr_in_dynamic_section) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x500);
+ interface->MockSetDataVaddrEnd(0x600);
+ interface->MockSetDataOffset(0xa000);
+
+ interface->MockSetDynamicVaddrStart(0x800);
+ interface->MockSetDynamicVaddrEnd(0x900);
+ interface->MockSetDynamicOffset(0xc000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x880), ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0xc080U, offset);
+}
+
+TEST_F(ElfTest, is_valid_pc_elf_invalid) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+
+ EXPECT_FALSE(elf.IsValidPc(0x100));
+ EXPECT_FALSE(elf.IsValidPc(0x200));
+}
+
+TEST_F(ElfTest, is_valid_pc_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, error_code_not_valid) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+
+ ErrorData error{ERROR_MEMORY_INVALID, 0x100};
+ elf.GetLastError(&error);
+ EXPECT_EQ(ERROR_MEMORY_INVALID, error.code);
+ EXPECT_EQ(0x100U, error.address);
+}
+
+TEST_F(ElfTest, error_code_valid) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+ elf.FakeSetInterface(interface);
+ interface->FakeSetErrorCode(ERROR_MEMORY_INVALID);
+ interface->FakeSetErrorAddress(0x1000);
+
+ ErrorData error{ERROR_NONE, 0};
+ elf.GetLastError(&error);
+ EXPECT_EQ(ERROR_MEMORY_INVALID, error.code);
+ EXPECT_EQ(0x1000U, error.address);
+ EXPECT_EQ(ERROR_MEMORY_INVALID, elf.GetLastErrorCode());
+ EXPECT_EQ(0x1000U, elf.GetLastErrorAddress());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
new file mode 100644
index 000000000..69163ac94
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine_type;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+std::string TestGetFileDirectory() {
+ std::string exec(testing::internal::GetArgvs()[0]);
+ auto const value = exec.find_last_of('/');
+ if (value == std::string::npos) {
+ return "tests/files/";
+ }
+ return exec.substr(0, value + 1) + "tests/files/";
+}
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
+ TestCopyFuncType copy_func) {
+ Ehdr ehdr;
+
+ TestInitEhdr(&ehdr, elf_class, machine);
+
+ uint64_t offset = sizeof(Ehdr);
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ copy_func(0, &ehdr, sizeof(ehdr));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ copy_func(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // Skip this header, it will contain the gnu_debugdata information.
+ uint64_t gnu_offset = offset;
+ offset += ehdr.e_shentsize;
+
+ uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 1;
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_offset = symtab_offset;
+ shdr.sh_size = 0x100;
+ copy_func(offset, &shdr, sizeof(shdr));
+
+ char value = '\0';
+ uint64_t symname_offset = symtab_offset;
+ copy_func(symname_offset, &value, 1);
+ symname_offset++;
+ std::string name(".shstrtab");
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+ symname_offset += name.size() + 1;
+ name = ".gnu_debugdata";
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+
+ ssize_t bytes = 0x100;
+ offset = symtab_offset + 0x100;
+ if (init_gnu_debugdata) {
+ // Read in the compressed elf data and copy it in.
+ name = TestGetFileDirectory();
+ if (elf_class == ELFCLASS32) {
+ name += "elf32.xz";
+ } else {
+ name += "elf64.xz";
+ }
+ int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
+ ASSERT_NE(-1, fd) << "Cannot open " + name;
+ // Assumes the file is less than 1024 bytes.
+ std::vector<uint8_t> buf(1024);
+ bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
+ ASSERT_GT(bytes, 0);
+ // Make sure the file isn't too big.
+ ASSERT_NE(static_cast<size_t>(bytes), buf.size())
+ << "File " + name + " is too big, increase buffer size.";
+ close(fd);
+ buf.resize(bytes);
+ copy_func(offset, buf.data(), buf.size());
+ }
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = symname_offset - symtab_offset;
+ shdr.sh_addr = offset;
+ shdr.sh_offset = offset;
+ shdr.sh_size = bytes;
+ copy_func(gnu_offset, &shdr, sizeof(shdr));
+}
+
+template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t);
+template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t);
+
+template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
new file mode 100644
index 000000000..62cd59a29
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+
+#include <functional>
+#include <string>
+
+namespace unwindstack {
+
+typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type);
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
+ TestCopyFuncType copy_func);
+
+std::string TestGetFileDirectory();
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GenGnuDebugdata.cpp b/libunwindstack/tests/GenGnuDebugdata.cpp
new file mode 100644
index 000000000..2644582b2
--- /dev/null
+++ b/libunwindstack/tests/GenGnuDebugdata.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+template <typename Ehdr>
+void InitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+template <typename Ehdr, typename Shdr>
+void GenElf(Ehdr* ehdr, int fd) {
+ uint64_t offset = sizeof(Ehdr);
+ ehdr->e_shoff = offset;
+ ehdr->e_shnum = 3;
+ ehdr->e_shentsize = sizeof(Shdr);
+ ehdr->e_shstrndx = 2;
+ TEMP_FAILURE_RETRY(write(fd, ehdr, sizeof(Ehdr)));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 0;
+ shdr.sh_type = SHT_NULL;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 11;
+ shdr.sh_addr = 0x5000;
+ shdr.sh_offset = 0x5000;
+ shdr.sh_entsize = 0x100;
+ shdr.sh_size = 0x800;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+ offset += ehdr->e_shentsize;
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x200;
+ shdr.sh_size = 24;
+ TEMP_FAILURE_RETRY(write(fd, &shdr, sizeof(Shdr)));
+
+ // Write out the name entries information.
+ lseek(fd, 0x200, SEEK_SET);
+ std::string name;
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+ name = ".shstrtab";
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+ name = ".debug_frame";
+ TEMP_FAILURE_RETRY(write(fd, name.data(), name.size() + 1));
+}
+
+int main() {
+ int elf32_fd = TEMP_FAILURE_RETRY(open("elf32", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (elf32_fd == -1) {
+ printf("Failed to create elf32: %s\n", strerror(errno));
+ return 1;
+ }
+
+ int elf64_fd = TEMP_FAILURE_RETRY(open("elf64", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (elf64_fd == -1) {
+ printf("Failed to create elf64: %s\n", strerror(errno));
+ return 1;
+ }
+
+ Elf32_Ehdr ehdr32;
+ InitEhdr<Elf32_Ehdr>(&ehdr32, ELFCLASS32, EM_ARM);
+ GenElf<Elf32_Ehdr, Elf32_Shdr>(&ehdr32, elf32_fd);
+ close(elf32_fd);
+
+ Elf64_Ehdr ehdr64;
+ InitEhdr<Elf64_Ehdr>(&ehdr64, ELFCLASS64, EM_AARCH64);
+ GenElf<Elf64_Ehdr, Elf64_Shdr>(&ehdr64, elf64_fd);
+ close(elf64_fd);
+}
diff --git a/libunwindstack/tests/IsolatedSettings.cpp b/libunwindstack/tests/IsolatedSettings.cpp
new file mode 100644
index 000000000..dbd8bd608
--- /dev/null
+++ b/libunwindstack/tests/IsolatedSettings.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+ static const char* initial_args[2] = {"--slow_threshold_ms=90000",
+ "--deadline_threshold_ms=120000"};
+ *args = initial_args;
+ *num_args = 2;
+ return true;
+}
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
new file mode 100644
index 000000000..9b32a3a43
--- /dev/null
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class JitDebugTest : public ::testing::Test {
+ protected:
+ void CreateFakeElf(MapInfo* map_info, uint64_t global_offset, uint64_t data_offset,
+ uint64_t data_vaddr, uint64_t data_size) {
+ MemoryFake* memory = new MemoryFake;
+ ElfFake* elf = new ElfFake(memory);
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", global_offset);
+ interface->FakeSetDataOffset(data_offset);
+ interface->FakeSetDataVaddrStart(data_vaddr);
+ interface->FakeSetDataVaddrEnd(data_vaddr + data_size);
+ map_info->elf.reset(elf);
+ }
+
+ void Init(ArchEnum arch) {
+ jit_debug_.reset(new JitDebug(process_memory_));
+ jit_debug_->SetArch(arch);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
+ "4000-6000 r--s 00000000 00:00 0 /fake/elf1\n"
+ "6000-8000 -wxs 00002000 00:00 0 /fake/elf1\n"
+ "a000-c000 --xp 00000000 00:00 0 /fake/elf2\n"
+ "c000-f000 rw-p 00002000 00:00 0 /fake/elf2\n"
+ "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+ "11000-12000 rw-p 00002000 00:00 0 /fake/elf3\n"
+ "12000-14000 r--p 00000000 00:00 0 /fake/elf4\n"
+ "100000-110000 rw-p 00ee000 00:00 0 /fake/elf4\n"
+ "200000-210000 rw-p 01ee000 00:00 0 /fake/elf4\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ MapInfo* map_info = maps_->Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
+
+ map_info = maps_->Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
+
+ map_info = maps_->Get(7);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0xee800, 0xee000, 0xee000, 0x10000);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
+ }
+
+ template <typename EhdrType, typename ShdrType>
+ void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc,
+ uint32_t size) {
+ EhdrType ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ uint64_t sh_offset = sizeof(ehdr);
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_machine = machine_type;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(ShdrType);
+ ehdr.e_shnum = 3;
+ memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+ ShdrType shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+ memory_->SetMemory(offset + 0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ // Now add a single cie/fde.
+ uint64_t dwarf_offset = offset + 0x600;
+ if (class_type == ELFCLASS32) {
+ // CIE 32 information.
+ memory_->SetData32(dwarf_offset, 0xfc);
+ memory_->SetData32(dwarf_offset + 0x4, 0xffffffff);
+ memory_->SetData8(dwarf_offset + 0x8, 1);
+ memory_->SetData8(dwarf_offset + 0x9, '\0');
+ memory_->SetData8(dwarf_offset + 0xa, 0x4);
+ memory_->SetData8(dwarf_offset + 0xb, 0x4);
+ memory_->SetData8(dwarf_offset + 0xc, 0x1);
+
+ // FDE 32 information.
+ memory_->SetData32(dwarf_offset + 0x100, 0xfc);
+ memory_->SetData32(dwarf_offset + 0x104, 0);
+ memory_->SetData32(dwarf_offset + 0x108, pc);
+ memory_->SetData32(dwarf_offset + 0x10c, size);
+ } else {
+ // CIE 64 information.
+ memory_->SetData32(dwarf_offset, 0xffffffff);
+ memory_->SetData64(dwarf_offset + 4, 0xf4);
+ memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+ memory_->SetData8(dwarf_offset + 0x14, 1);
+ memory_->SetData8(dwarf_offset + 0x15, '\0');
+ memory_->SetData8(dwarf_offset + 0x16, 0x4);
+ memory_->SetData8(dwarf_offset + 0x17, 0x4);
+ memory_->SetData8(dwarf_offset + 0x18, 0x1);
+
+ // FDE 64 information.
+ memory_->SetData32(dwarf_offset + 0x100, 0xffffffff);
+ memory_->SetData64(dwarf_offset + 0x104, 0xf4);
+ memory_->SetData64(dwarf_offset + 0x10c, 0);
+ memory_->SetData64(dwarf_offset + 0x114, pc);
+ memory_->SetData64(dwarf_offset + 0x11c, size);
+ }
+ }
+
+ void WriteDescriptor32(uint64_t addr, uint32_t entry);
+ void WriteDescriptor64(uint64_t addr, uint64_t entry);
+ void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size);
+ void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size);
+ void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+ uint64_t elf_size);
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+ std::unique_ptr<JitDebug> jit_debug_;
+ std::unique_ptr<BufferMaps> maps_;
+};
+
+void JitDebugTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
+ // Format of the 32 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint32_t relevant_entry
+ memory_->SetData32(addr + 8, 0);
+ // uint32_t first_entry
+ memory_->SetData32(addr + 12, entry);
+}
+
+void JitDebugTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
+ // Format of the 64 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint64_t relevant_entry
+ memory_->SetData64(addr + 8, 0);
+ // uint64_t first_entry
+ memory_->SetData64(addr + 16, entry);
+}
+
+void JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory_->SetData32(addr + 8, elf_addr);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 12, elf_size);
+}
+
+void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory_->SetData32(addr + 8, elf_addr);
+ // uint32_t pad
+ memory_->SetData32(addr + 12, 0);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 16, elf_size);
+}
+
+void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 64 bit JITCodeEntry structure:
+ // uint64_t next
+ memory_->SetData64(addr, next);
+ // uint64_t prev
+ memory_->SetData64(addr + 8, prev);
+ // uint64_t symfile_addr
+ memory_->SetData64(addr + 16, elf_addr);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 24, elf_size);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid) {
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_global_variable) {
+ maps_.reset(new BufferMaps(""));
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0x11800, 0x200000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0x11800, 0);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0x11800, 0x20000);
+ // Set the version to an invalid value.
+ memory_->SetData32(0x11800, 2);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_32) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0x11800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_multiple_jit_debug_descriptors_valid) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2000, 0x300);
+
+ WriteDescriptor32(0x11800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+ WriteDescriptor32(0x100800, 0x201000);
+ WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000);
+
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr);
+
+ // Now clear the descriptor entry for the first one.
+ WriteDescriptor32(0x11800, 0);
+ jit_debug_.reset(new JitDebug(process_memory_));
+ jit_debug_->SetArch(ARCH_ARM);
+
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) != nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_x86) {
+ Init(ARCH_X86);
+
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0x11800, 0x200000);
+ WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
+
+ jit_debug_->SetArch(ARCH_X86);
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_64) {
+ Init(ARCH_ARM64);
+
+ CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
+
+ WriteDescriptor64(0x11800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_multiple_entries) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
+
+ WriteDescriptor32(0x11800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
+ WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
+
+ Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
+ ASSERT_TRUE(elf_2 != nullptr);
+
+ Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
+ ASSERT_TRUE(elf_1 != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
+ EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
+ EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
+ EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
+ EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
+ EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
+}
+
+TEST_F(JitDebugTest, get_elf_search_libs) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0x11800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+ // Only search a given named list of libs.
+ std::vector<std::string> libs{"libart.so"};
+ jit_debug_.reset(new JitDebug(process_memory_, libs));
+ jit_debug_->SetArch(ARCH_ARM);
+ EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+
+ // Change the name of the map that includes the value and verify this works.
+ MapInfo* map_info = maps_->Get(5);
+ map_info->name = "/system/lib/libart.so";
+ map_info = maps_->Get(6);
+ map_info->name = "/system/lib/libart.so";
+ jit_debug_.reset(new JitDebug(process_memory_, libs));
+ // Make sure that clearing our copy of the libs doesn't affect the
+ // JitDebug object.
+ libs.clear();
+ jit_debug_->SetArch(ARCH_ARM);
+ EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 000000000..9936f7afa
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+ g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+ SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+ SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+ SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+ const std::vector<LocalFrameData>& frame_info) {
+ std::string unwind;
+ size_t i = 0;
+ for (const auto& frame : frame_info) {
+ unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+ frame.pc, frame.rel_pc);
+ if (frame.map_info != nullptr) {
+ if (!frame.map_info->name.empty()) {
+ unwind += " " + frame.map_info->name;
+ } else {
+ unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+ frame.map_info->end);
+ }
+ if (frame.map_info->offset != 0) {
+ unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+ }
+ }
+ if (!frame.function_name.empty()) {
+ unwind += " " + frame.function_name;
+ if (frame.function_offset != 0) {
+ unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ }
+ unwind += '\n';
+ }
+
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ std::vector<LocalFrameData> frame_info;
+ g_frame_info = &frame_info;
+ g_unwinder = unwinder;
+ std::vector<const char*> expected_function_names;
+
+ if (unwind_through_signal) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLocalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ raise(SIGUSR1);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction",
+ "LocalInnerFunction", "SignalLocalOuterFunction",
+ "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+ } else {
+ ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+ }
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ unwinder_.reset(new LocalUnwinder);
+ ASSERT_TRUE(unwinder_->Init());
+ }
+
+ std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+ LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+ LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+ // Prime the maps data.
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ std::string testlib(testing::internal::GetArgvs()[0]);
+ auto const value = testlib.find_last_of('/');
+ if (value != std::string::npos) {
+ testlib = testlib.substr(0, value + 1);
+ } else {
+ testlib = "";
+ }
+ testlib += "libunwindstack_local.so";
+
+ void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+
+ void (*unwind_function)(void*, void*) =
+ reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+ ASSERT_TRUE(unwind_function != nullptr);
+
+ std::vector<LocalFrameData> frame_info;
+ unwind_function(unwinder_.get(), &frame_info);
+
+ ASSERT_EQ(0, dlclose(handle));
+
+ std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+ "TestlibLevel3", "TestlibLevel4"};
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
new file mode 100644
index 000000000..99afb0b23
--- /dev/null
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
+
+class TestUpdatableMaps : public LocalUpdatableMaps {
+ public:
+ TestUpdatableMaps() : LocalUpdatableMaps() {}
+ virtual ~TestUpdatableMaps() = default;
+
+ const std::string GetMapsFile() const override { return maps_file_; }
+
+ void TestSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
+
+ const std::vector<std::unique_ptr<MapInfo>>& TestGetSavedMaps() { return saved_maps_; }
+
+ private:
+ std::string maps_file_;
+};
+
+class LocalUpdatableMapsTest : public ::testing::Test {
+ protected:
+ static const std::string GetDefaultMapString() {
+ return "3000-4000 r-xp 00000 00:00 0\n8000-9000 r-xp 00000 00:00 0\n";
+ }
+
+ void SetUp() override {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFile(GetDefaultMapString(), tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Parse());
+ ASSERT_EQ(2U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(0);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(1);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+ }
+
+ TestUpdatableMaps maps_;
+};
+
+TEST_F(LocalUpdatableMapsTest, same_map) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFile(GetDefaultMapString(), tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(2U, maps_.Total());
+ EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
+
+ MapInfo* map_info = maps_.Get(0);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(1);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, same_map_new_perms) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+ "8000-9000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(2U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(0);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(1);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ auto& saved_maps = maps_.TestGetSavedMaps();
+ ASSERT_EQ(1U, saved_maps.size());
+ map_info = saved_maps[0].get();
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, same_map_new_name) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "8000-9000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(2U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(0);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib.so", map_info->name);
+
+ map_info = maps_.Get(1);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ auto& saved_maps = maps_.TestGetSavedMaps();
+ ASSERT_EQ(1U, saved_maps.size());
+ map_info = saved_maps[0].get();
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, only_add_maps) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("1000-2000 r-xp 00000 00:00 0\n"
+ "3000-4000 r-xp 00000 00:00 0\n"
+ "8000-9000 r-xp 00000 00:00 0\n"
+ "a000-f000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(4U, maps_.Total());
+ EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
+
+ MapInfo* map_info = maps_.Get(0);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x1000U, map_info->start);
+ EXPECT_EQ(0x2000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(1);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0xa000U, map_info->start);
+ EXPECT_EQ(0xf000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, all_new_maps) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("1000-2000 r-xp 00000 00:00 0\n"
+ "a000-f000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(2U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(0);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x1000U, map_info->start);
+ EXPECT_EQ(0x2000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = maps_.Get(1);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0xa000U, map_info->start);
+ EXPECT_EQ(0xf000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ auto& saved_maps = maps_.TestGetSavedMaps();
+ ASSERT_EQ(2U, saved_maps.size());
+ map_info = saved_maps[0].get();
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x3000U, map_info->start);
+ EXPECT_EQ(0x4000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+
+ map_info = saved_maps[1].get();
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+}
+
+TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+ "8000-9000 r-xp 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(3U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+}
+
+TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(4U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+
+ map_info = maps_.Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_TRUE(map_info->IsBlank());
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_map);
+ EXPECT_TRUE(map_info->name.empty());
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
+ "a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(6U, maps_.Total());
+
+ map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+
+ map_info = maps_.Get(4);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib2.so", map_info->name);
+ EXPECT_EQ(maps_.Get(3), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+
+ map_info = maps_.Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0xA000U, map_info->start);
+ EXPECT_EQ(0xB000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib3.so", map_info->name);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_real_map);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
new file mode 100644
index 000000000..537ccafb3
--- /dev/null
+++ b/libunwindstack/tests/LogFake.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+
+#include "LogFake.h"
+
+// Forward declarations.
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+namespace unwindstack {
+
+void ResetLogs() {
+ g_fake_log_buf = "";
+ g_fake_log_print = "";
+}
+
+std::string GetFakeLogBuf() {
+ return g_fake_log_buf;
+}
+
+std::string GetFakeLogPrint() {
+ return g_fake_log_print;
+}
+
+} // namespace unwindstack
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+ g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+ g_fake_log_buf += tag;
+ g_fake_log_buf += ' ';
+ g_fake_log_buf += msg;
+ return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int val = __android_log_vprint(prio, tag, fmt, ap);
+ va_end(ap);
+
+ return val;
+}
+
+extern "C" int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+ g_fake_log_print += std::to_string(prio) + ' ';
+ g_fake_log_print += tag;
+ g_fake_log_print += ' ';
+
+ android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+
+ g_fake_log_print += '\n';
+
+ return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+ return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+ errno = EACCES;
+ return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+ return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+ return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+ struct logger_entry*,
+ AndroidLogEntry*, const EventTagMap*, char*, int) {
+ return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
new file mode 100644
index 000000000..e1dc50d9c
--- /dev/null
+++ b/libunwindstack/tests/LogFake.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+
+#include <string>
+
+namespace unwindstack {
+
+void ResetLogs();
+std::string GetFakeLogBuf();
+std::string GetFakeLogPrint();
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
new file mode 100644
index 000000000..6d8d58e55
--- /dev/null
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoCreateMemoryTest : public ::testing::Test {
+ protected:
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
+ std::vector<uint8_t> buffer(20000);
+ memset(buffer.data(), 0, buffer.size());
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
+ }
+
+ void SetUp() override {
+ std::vector<uint8_t> buffer(12288, 0);
+ memcpy(buffer.data(), ELFMAG, SELFMAG);
+ buffer[EI_CLASS] = ELFCLASS32;
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), 1024));
+
+ memset(buffer.data(), 0, buffer.size());
+ memcpy(&buffer[0x1000], ELFMAG, SELFMAG);
+ buffer[0x1000 + EI_CLASS] = ELFCLASS64;
+ buffer[0x2000] = 0xff;
+ ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, buffer.data(), buffer.size()));
+
+ InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
+ InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
+
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+ }
+
+ MemoryFake* memory_;
+ std::shared_ptr<Memory> process_memory_;
+
+ TemporaryFile elf_;
+
+ TemporaryFile elf_at_1000_;
+
+ TemporaryFile elf32_at_map_;
+ TemporaryFile elf64_at_map_;
+};
+
+TEST_F(MapInfoCreateMemoryTest, end_le_start) {
+ MapInfo info(nullptr, nullptr, 0x100, 0x100, 0, 0, elf_.path);
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ info.end = 0xff;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ // Make sure this test is valid.
+ info.end = 0x101;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
+ MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ // Read the entire file.
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 1024));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
+
+ // Now verify the elf start offset is set correctly based on the previous
+ // info.
+ MapInfo prev_info(nullptr, nullptr, 0, 0x100, 0x10, 0, "");
+ info.prev_map = &prev_info;
+ info.prev_real_map = &prev_info;
+
+ // No preconditions met, change each one until it should set the elf start
+ // offset to zero.
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.offset = 0;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.flags = PROT_READ;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.name = info.name;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0U, info.elf_start_offset);
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
+ MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
+
+ // Read the valid part of the file.
+ std::vector<uint8_t> buffer(0x100);
+ ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->ReadFully(0x100, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0x2000U, info.elf_start_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
+ // Set up some memory so that a valid local memory object would
+ // be returned if the file mapping fails, but the device check is incorrect.
+ std::vector<uint8_t> buffer(1024);
+ uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+ MapInfo info(nullptr, nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoCreateMemoryTest, process_memory) {
+ MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
+
+ // Verify that the the process_memory object is used, so seed it
+ // with memory.
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+ buffer[i] = i % 256;
+ }
+ memory_->SetMemory(info.start, buffer.data(), buffer.size());
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_TRUE(info.memory_backed_elf);
+
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+ ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
+ }
+
+ // Try to read outside of the map size.
+ ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+ maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+ // Set the memory in the r-x map.
+ memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+ MapInfo* map_info = maps.Find(0x3000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_TRUE(map_info->memory_backed_elf);
+ EXPECT_EQ(0x4000UL, map_info->elf_offset);
+ EXPECT_EQ(0x4000UL, map_info->offset);
+ EXPECT_EQ(0U, map_info->elf_start_offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1600UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+ maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+ // Setup an elf at offset 0x1000 in memory.
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+ memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+ // Setup an elf at offset 0x3000 in memory..
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+ memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+ MapInfo* map_info = maps.Find(0x4000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_TRUE(map_info->memory_backed_elf);
+ EXPECT_EQ(0x1000UL, map_info->elf_offset);
+ EXPECT_EQ(0xb000UL, map_info->offset);
+ EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
+ maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
+
+ MapInfo* map_info = maps.Find(0x2000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ // Set up the size
+ Elf64_Ehdr ehdr;
+ ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+ // Will not give the elf memory, because the read-only entry does not
+ // extend over the executable segment.
+ std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(map_info->memory_backed_elf);
+ std::vector<uint8_t> buffer(0x100);
+ EXPECT_EQ(0x2000U, map_info->offset);
+ EXPECT_EQ(0U, map_info->elf_offset);
+ EXPECT_EQ(0U, map_info->elf_start_offset);
+ ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
+ EXPECT_EQ(0xffU, buffer[0]);
+
+ // Now init the elf data enough so that the file memory object will be used.
+ ehdr.e_shoff = 0x4000;
+ ehdr.e_shnum = 1;
+ ehdr.e_shentsize = 0x100;
+ ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+ ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+ map_info->memory_backed_elf = false;
+ memory.reset(map_info->CreateMemory(process_memory_));
+ EXPECT_FALSE(map_info->memory_backed_elf);
+ EXPECT_EQ(0x2000U, map_info->offset);
+ EXPECT_EQ(0x1000U, map_info->elf_offset);
+ EXPECT_EQ(0x1000U, map_info->elf_start_offset);
+ Elf64_Ehdr ehdr_mem;
+ ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
+ EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
new file mode 100644
index 000000000..6953e26f9
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/test_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetBuildIDTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ tf_.reset(new TemporaryFile);
+
+ memory_ = new MemoryFake;
+ elf_ = new ElfFake(new MemoryFake);
+ elf_interface_ = new ElfInterfaceFake(memory_);
+ elf_->FakeSetInterface(elf_interface_);
+ elf_container_.reset(elf_);
+ map_info_.reset(
+ new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+ }
+
+ void MultipleThreadTest(std::string expected_build_id);
+
+ MemoryFake* memory_;
+ ElfFake* elf_;
+ ElfInterfaceFake* elf_interface_;
+ std::unique_ptr<ElfFake> elf_container_;
+ std::unique_ptr<MapInfo> map_info_;
+ std::unique_ptr<TemporaryFile> tf_;
+};
+
+TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+ EXPECT_EQ("", info.GetBuildID());
+ EXPECT_EQ("", info.GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf) {
+ map_info_->elf.reset(elf_container_.release());
+ elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+ EXPECT_EQ("FAKE_BUILD_ID", map_info_->GetBuildID());
+ EXPECT_EQ("46414b455f4255494c445f4944", map_info_->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf_no_sign_extension) {
+ map_info_->elf.reset(elf_container_.release());
+
+ std::string build_id = {static_cast<char>(0xfa), static_cast<char>(0xab), static_cast<char>(0x12),
+ static_cast<char>(0x02)};
+ elf_interface_->FakeSetBuildID(build_id);
+
+ EXPECT_EQ("\xFA\xAB\x12\x2", map_info_->GetBuildID());
+ EXPECT_EQ("faab1202", map_info_->GetPrintableBuildID());
+}
+
+void MapInfoGetBuildIDTest::MultipleThreadTest(std::string expected_build_id) {
+ static constexpr size_t kNumConcurrentThreads = 100;
+
+ std::string build_id_values[kNumConcurrentThreads];
+ std::vector<std::thread*> threads;
+
+ std::atomic_bool wait;
+ wait = true;
+ // Create all of the threads and have them do the GetLoadBias at the same time
+ // to make it likely that a race will occur.
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ std::thread* thread = new std::thread([i, this, &wait, &build_id_values]() {
+ while (wait)
+ ;
+ build_id_values[i] = map_info_->GetBuildID();
+ });
+ threads.push_back(thread);
+ }
+
+ // Set them all going and wait for the threads to finish.
+ wait = false;
+ for (auto thread : threads) {
+ thread->join();
+ delete thread;
+ }
+
+ // Now verify that all of the elf files are exactly the same and valid.
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ EXPECT_EQ(expected_build_id, build_id_values[i]) << "Thread " << i << " mismatched.";
+ }
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists) {
+ map_info_->elf.reset(elf_container_.release());
+ elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+ MultipleThreadTest("FAKE_BUILD_ID");
+}
+
+static void InitElfData(int fd) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shstrndx = 2;
+ off_t offset = 0;
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(ehdr)), write(fd, &ehdr, sizeof(ehdr)));
+
+ char note_section[128];
+ Elf32_Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 12; // "ELF_BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(&note_section, &note_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ memcpy(&note_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ memcpy(&note_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID"));
+ note_offset += sizeof("ELF_BUILDID");
+
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_section);
+ offset += ehdr.e_shoff + sizeof(shdr);
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ offset += sizeof(shdr);
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+ offset = 0xf500;
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(".note.gnu.build-id")),
+ write(fd, ".note.gnu.build-id", sizeof(".note.gnu.build-id")));
+
+ offset = 0xb000;
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(note_section)),
+ write(fd, note_section, sizeof(note_section)));
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_memory) {
+ InitElfData(tf_->fd);
+
+ EXPECT_EQ("ELF_BUILDID", map_info_->GetBuildID());
+ EXPECT_EQ("454c465f4255494c444944", map_info_->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists_in_memory) {
+ InitElfData(tf_->fd);
+
+ MultipleThreadTest("ELF_BUILDID");
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
new file mode 100644
index 000000000..7f9781491
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetElfTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+ }
+
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(uint64_t sh_offset, Ehdr* ehdr, uint8_t class_type, uint8_t machine_type) {
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = class_type;
+ ehdr->e_machine = machine_type;
+ ehdr->e_shoff = sh_offset;
+ ehdr->e_shentsize = sizeof(Shdr) + 100;
+ ehdr->e_shnum = 4;
+ }
+
+ const size_t kMapSize = 4096;
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+
+ TemporaryFile elf_;
+};
+
+TEST_F(MapInfoGetElfTest, invalid) {
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+ // The map is empty, but this should still create an invalid elf object.
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid32) {
+ MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+
+ // Now verify that an empty process memory returns an invalid elf object.
+ info.elf.reset();
+ elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid64) {
+ MapInfo info(nullptr, nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
+
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
+ MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
+ MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x2000 + offset, ptr, size);
+ });
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
+ MapInfo info(nullptr, nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
+
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x5000 + offset, ptr, size);
+ });
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, end_le_start) {
+ MapInfo info(nullptr, nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+
+ info.elf.reset();
+ info.end = 0xfff;
+ elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+
+ // Make sure this test is valid.
+ info.elf.reset();
+ info.end = 0x2000;
+ elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+
+ std::vector<uint8_t> buffer(0x1000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+
+ // Read the entire file.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), buffer.size()));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(elf->memory()->ReadFully(buffer.size(), buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Read the valid part of the file.
+ ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, check_device_maps) {
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/something");
+
+ // Create valid elf data in process memory for this to verify that only
+ // the name is causing invalid elf data.
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 0;
+ memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+
+ // Set the name to nothing to verify that it still fails.
+ info.elf.reset();
+ info.name = "";
+ elf = info.GetElf(process_memory_, ARCH_X86_64);
+ ASSERT_FALSE(elf->valid());
+
+ // Change the flags and verify the elf is valid now.
+ info.elf.reset();
+ info.flags = PROT_READ;
+ elf = info.GetElf(process_memory_, ARCH_X86_64);
+ ASSERT_TRUE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) {
+ static constexpr size_t kNumConcurrentThreads = 100;
+
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 0;
+ memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+ Elf* elf_in_threads[kNumConcurrentThreads];
+ std::vector<std::thread*> threads;
+
+ std::atomic_bool wait;
+ wait = true;
+ // Create all of the threads and have them do the GetElf at the same time
+ // to make it likely that a race will occur.
+ MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
+ while (wait)
+ ;
+ Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
+ elf_in_threads[i] = elf;
+ });
+ threads.push_back(thread);
+ }
+ ASSERT_TRUE(info.elf == nullptr);
+
+ // Set them all going and wait for the threads to finish.
+ wait = false;
+ for (auto thread : threads) {
+ thread->join();
+ delete thread;
+ }
+
+ // Now verify that all of the elf files are exactly the same and valid.
+ Elf* elf = info.elf.get();
+ ASSERT_TRUE(elf != nullptr);
+ EXPECT_TRUE(elf->valid());
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ EXPECT_EQ(elf, elf_in_threads[i]) << "Thread " << i << " mismatched.";
+ }
+}
+
+// Verify that previous maps don't automatically get the same elf object.
+TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
+ MapInfo info1(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+ MapInfo info2(&info1, &info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
+ Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by a read-execute map will result
+// in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
+ MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo rw_info(&r_info, &r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by an empty map, then followed by
+// a read-execute map will result in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_empty_then_read_exec_share_elf) {
+ MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo empty(&r_info, &r_info, 0x2000, 0x3000, 0, 0, "");
+ MapInfo rw_info(&empty, &r_info, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
new file mode 100644
index 000000000..971d4520e
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetLoadBiasTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+ elf_ = new ElfFake(new MemoryFake);
+ elf_container_.reset(elf_);
+ map_info_.reset(new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+ }
+
+ void MultipleThreadTest(uint64_t expected_load_bias);
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+ ElfFake* elf_;
+ std::unique_ptr<ElfFake> elf_container_;
+ std::unique_ptr<MapInfo> map_info_;
+};
+
+TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
+ MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+ EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
+ map_info_->elf.reset(elf_container_.release());
+
+ elf_->FakeSetLoadBias(0);
+ EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+ elf_->FakeSetLoadBias(0x1000);
+ EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists) {
+ map_info_->elf.reset(elf_container_.release());
+
+ elf_->FakeSetLoadBias(0);
+ EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+ map_info_->load_bias = INT64_MAX;
+ elf_->FakeSetLoadBias(0x1000);
+ EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
+}
+
+void MapInfoGetLoadBiasTest::MultipleThreadTest(uint64_t expected_load_bias) {
+ static constexpr size_t kNumConcurrentThreads = 100;
+
+ uint64_t load_bias_values[kNumConcurrentThreads];
+ std::vector<std::thread*> threads;
+
+ std::atomic_bool wait;
+ wait = true;
+ // Create all of the threads and have them do the GetLoadBias at the same time
+ // to make it likely that a race will occur.
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ std::thread* thread = new std::thread([i, this, &wait, &load_bias_values]() {
+ while (wait)
+ ;
+ load_bias_values[i] = map_info_->GetLoadBias(process_memory_);
+ });
+ threads.push_back(thread);
+ }
+
+ // Set them all going and wait for the threads to finish.
+ wait = false;
+ for (auto thread : threads) {
+ thread->join();
+ delete thread;
+ }
+
+ // Now verify that all of the elf files are exactly the same and valid.
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ EXPECT_EQ(expected_load_bias, load_bias_values[i]) << "Thread " << i << " mismatched.";
+ }
+}
+
+TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists) {
+ map_info_->elf.reset(elf_container_.release());
+ elf_->FakeSetLoadBias(0x1000);
+
+ MultipleThreadTest(0x1000);
+}
+
+static void InitElfData(MemoryFake* memory, uint64_t offset) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_phoff = 0x5000;
+ ehdr.e_phnum = 2;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_NULL;
+ memory->SetMemory(offset + 0x5000, &phdr, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_flags = PF_X;
+ phdr.p_offset = 0;
+ phdr.p_vaddr = 0xe000;
+ memory->SetMemory(offset + 0x5000 + sizeof(phdr), &phdr, sizeof(phdr));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory) {
+ InitElfData(memory_, map_info_->start);
+
+ EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory_cached) {
+ InitElfData(memory_, map_info_->start);
+
+ EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+
+ memory_->Clear();
+ EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) {
+ InitElfData(memory_, map_info_->start);
+
+ MultipleThreadTest(0xe000);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 000000000..98edc0e7c
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "ElfFake.h"
+
+namespace unwindstack {
+
+TEST(MapInfoTest, maps_constructor_const_char) {
+ MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+ MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, "map");
+
+ EXPECT_EQ(&prev_map, map_info.prev_map);
+ EXPECT_EQ(1UL, map_info.start);
+ EXPECT_EQ(2UL, map_info.end);
+ EXPECT_EQ(3UL, map_info.offset);
+ EXPECT_EQ(4UL, map_info.flags);
+ EXPECT_EQ("map", map_info.name);
+ EXPECT_EQ(INT64_MAX, map_info.load_bias);
+ EXPECT_EQ(0UL, map_info.elf_offset);
+ EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, maps_constructor_string) {
+ std::string name("string_map");
+ MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
+ MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, name);
+
+ EXPECT_EQ(&prev_map, map_info.prev_map);
+ EXPECT_EQ(1UL, map_info.start);
+ EXPECT_EQ(2UL, map_info.end);
+ EXPECT_EQ(3UL, map_info.offset);
+ EXPECT_EQ(4UL, map_info.flags);
+ EXPECT_EQ("string_map", map_info.name);
+ EXPECT_EQ(INT64_MAX, map_info.load_bias);
+ EXPECT_EQ(0UL, map_info.elf_offset);
+ EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, get_function_name) {
+ ElfFake* elf = new ElfFake(nullptr);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ elf->FakeSetInterface(interface);
+ interface->FakePushFunctionData(FunctionData("function", 1000));
+
+ MapInfo map_info(nullptr, nullptr, 1, 2, 3, 4, "");
+ map_info.elf.reset(elf);
+
+ std::string name;
+ uint64_t offset;
+ ASSERT_TRUE(map_info.GetFunctionName(1000, &name, &offset));
+ EXPECT_EQ("function", name);
+ EXPECT_EQ(1000UL, offset);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
new file mode 100644
index 000000000..724eeb513
--- /dev/null
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
+
+static void VerifyLine(std::string line, MapInfo* info) {
+ BufferMaps maps(line.c_str());
+
+ if (info == nullptr) {
+ ASSERT_FALSE(maps.Parse()) << "Failed on: " + line;
+ } else {
+ ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
+ MapInfo* element = maps.Get(0);
+ ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
+ info->start = element->start;
+ info->end = element->end;
+ info->offset = element->offset;
+ info->flags = element->flags;
+ info->name = element->name;
+ info->elf_offset = element->elf_offset;
+ }
+}
+
+TEST(MapsTest, map_add) {
+ Maps maps;
+
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+ maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+ maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+ ASSERT_EQ(3U, maps.Total());
+ MapInfo* info = maps.Get(0);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("fake_map", info->name);
+ ASSERT_EQ(0U, info->elf_offset);
+ ASSERT_EQ(0U, info->load_bias.load());
+}
+
+TEST(MapsTest, map_move) {
+ Maps maps;
+
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+ maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+ maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+ Maps maps2 = std::move(maps);
+
+ ASSERT_EQ(3U, maps2.Total());
+ MapInfo* info = maps2.Get(0);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("fake_map", info->name);
+ ASSERT_EQ(0U, info->elf_offset);
+ ASSERT_EQ(0U, info->load_bias.load());
+}
+
+TEST(MapsTest, verify_parse_line) {
+ MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
+
+ VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
+ EXPECT_EQ(1U, info.start);
+ EXPECT_EQ(2U, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(3U, info.offset);
+ EXPECT_EQ("", info.name);
+
+ VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", &info);
+ EXPECT_EQ(0xaU, info.start);
+ EXPECT_EQ(0xbU, info.end);
+ EXPECT_EQ(0U, info.flags);
+ EXPECT_EQ(0xcU, info.offset);
+ EXPECT_EQ("/fake/name", info.name);
+
+ VerifyLine("01-02 rwxp 03 04:05 06 /fake/name/again\n", &info);
+ EXPECT_EQ(1U, info.start);
+ EXPECT_EQ(2U, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(3U, info.offset);
+ EXPECT_EQ("/fake/name/again", info.name);
+
+ VerifyLine("-00 rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00- rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 :00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00:00 \n", nullptr);
+ VerifyLine("x-00 rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00 -00 rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00x rwxp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwp 00 00:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 0000:00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00 :00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00: 00 0\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00:000\n", nullptr);
+ VerifyLine("00-00 rwxp 00 00:00 0/fake\n", nullptr);
+ VerifyLine("00-00 xxxx 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 ywxp 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 ryxp 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 rwyp 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("00-00 rwx- 00 00:00 0 /fake\n", nullptr);
+ VerifyLine("0\n", nullptr);
+ VerifyLine("00\n", nullptr);
+ VerifyLine("00-\n", nullptr);
+ VerifyLine("00-0\n", nullptr);
+ VerifyLine("00-00\n", nullptr);
+ VerifyLine("00-00 \n", nullptr);
+ VerifyLine("00-00 -\n", nullptr);
+ VerifyLine("00-00 r\n", nullptr);
+ VerifyLine("00-00 --\n", nullptr);
+ VerifyLine("00-00 rw\n", nullptr);
+ VerifyLine("00-00 ---\n", nullptr);
+ VerifyLine("00-00 rwx\n", nullptr);
+ VerifyLine("00-00 ---s\n", nullptr);
+ VerifyLine("00-00 ---p\n", nullptr);
+ VerifyLine("00-00 ---s 0\n", nullptr);
+ VerifyLine("00-00 ---p 0 \n", nullptr);
+ VerifyLine("00-00 ---p 0 0\n", nullptr);
+ VerifyLine("00-00 ---p 0 0:\n", nullptr);
+ VerifyLine("00-00 ---p 0 0:0\n", nullptr);
+ VerifyLine("00-00 ---p 0 0:0 \n", nullptr);
+
+ // Line to verify that the parser will detect a completely malformed line
+ // properly.
+ VerifyLine("7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n",
+ nullptr);
+}
+
+TEST(MapsTest, verify_large_values) {
+ MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
+#if defined(__LP64__)
+ VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
+ EXPECT_EQ(0xfabcdef012345678UL, info.start);
+ EXPECT_EQ(0xf12345678abcdef8UL, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(0xf0b0d0f010305070UL, info.offset);
+#else
+ VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", &info);
+ EXPECT_EQ(0xf2345678UL, info.start);
+ EXPECT_EQ(0xfabcdef8UL, info.end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+ EXPECT_EQ(0xf0305070UL, info.offset);
+#endif
+}
+
+TEST(MapsTest, parse_permissions) {
+ BufferMaps maps(
+ "1000-2000 ---s 00000000 00:00 0\n"
+ "2000-3000 r--s 00000000 00:00 0\n"
+ "3000-4000 -w-s 00000000 00:00 0\n"
+ "4000-5000 --xp 00000000 00:00 0\n"
+ "5000-6000 rwxp 00000000 00:00 0\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+
+ MapInfo* info = maps.Get(0);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(PROT_NONE, info->flags);
+ EXPECT_EQ(0x1000U, info->start);
+ EXPECT_EQ(0x2000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ("", info->name);
+
+ info = maps.Get(1);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(PROT_READ, info->flags);
+ EXPECT_EQ(0x2000U, info->start);
+ EXPECT_EQ(0x3000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ("", info->name);
+
+ info = maps.Get(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(PROT_WRITE, info->flags);
+ EXPECT_EQ(0x3000U, info->start);
+ EXPECT_EQ(0x4000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ("", info->name);
+
+ info = maps.Get(3);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(PROT_EXEC, info->flags);
+ EXPECT_EQ(0x4000U, info->start);
+ EXPECT_EQ(0x5000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ("", info->name);
+
+ info = maps.Get(4);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+ EXPECT_EQ(0x5000U, info->start);
+ EXPECT_EQ(0x6000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ("", info->name);
+
+ ASSERT_TRUE(maps.Get(5) == nullptr);
+}
+
+TEST(MapsTest, parse_name) {
+ BufferMaps maps(
+ "7b29b000-7b29e000 rw-p 00000000 00:00 0\n"
+ "7b29e000-7b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "7b29f000-7b2a0000 rw-p 00000000 00:00 0");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+
+ MapInfo* info = maps.Get(0);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ("", info->name);
+ EXPECT_EQ(0x7b29b000U, info->start);
+ EXPECT_EQ(0x7b29e000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+ info = maps.Get(1);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ("/system/lib/fake.so", info->name);
+ EXPECT_EQ(0x7b29e000U, info->start);
+ EXPECT_EQ(0x7b29f000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+ info = maps.Get(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ("", info->name);
+ EXPECT_EQ(0x7b29f000U, info->start);
+ EXPECT_EQ(0x7b2a0000U, info->end);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+ ASSERT_TRUE(maps.Get(3) == nullptr);
+}
+
+TEST(MapsTest, parse_offset) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(2U, maps.Total());
+
+ MapInfo* info = maps.Get(0);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0U, info->offset);
+ EXPECT_EQ(0xa000U, info->start);
+ EXPECT_EQ(0xe000U, info->end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+ EXPECT_EQ("/system/lib/fake.so", info->name);
+
+ info = maps.Get(1);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0xa12345U, info->offset);
+ EXPECT_EQ(0xe000U, info->start);
+ EXPECT_EQ(0xf000U, info->end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+ EXPECT_EQ("/system/lib/fake.so", info->name);
+
+ ASSERT_TRUE(maps.Get(2) == nullptr);
+}
+
+TEST(MapsTest, iterate) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(2U, maps.Total());
+
+ Maps::iterator it = maps.begin();
+ EXPECT_EQ(0xa000U, (*it)->start);
+ EXPECT_EQ(0xe000U, (*it)->end);
+ ++it;
+ EXPECT_EQ(0xe000U, (*it)->start);
+ EXPECT_EQ(0xf000U, (*it)->end);
+ ++it;
+ EXPECT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, const_iterate) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+ "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(2U, maps.Total());
+
+ Maps::const_iterator it = maps.begin();
+ EXPECT_EQ(0xa000U, (*it)->start);
+ EXPECT_EQ(0xe000U, (*it)->end);
+ ++it;
+ EXPECT_EQ(0xe000U, (*it)->start);
+ EXPECT_EQ(0xf000U, (*it)->end);
+ ++it;
+ EXPECT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, device) {
+ BufferMaps maps(
+ "a000-e000 rw-p 00000000 00:00 0 /dev/\n"
+ "f000-f100 rw-p 00000000 00:00 0 /dev/does_not_exist\n"
+ "f100-f200 rw-p 00000000 00:00 0 /dev/ashmem/does_not_exist\n"
+ "f200-f300 rw-p 00000000 00:00 0 /devsomething/does_not_exist\n");
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(4U, maps.Total());
+
+ MapInfo* info = maps.Get(0);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_TRUE(info->flags & 0x8000);
+ EXPECT_EQ("/dev/", info->name);
+
+ info = maps.Get(1);
+ EXPECT_TRUE(info->flags & 0x8000);
+ EXPECT_EQ("/dev/does_not_exist", info->name);
+
+ info = maps.Get(2);
+ EXPECT_FALSE(info->flags & 0x8000);
+ EXPECT_EQ("/dev/ashmem/does_not_exist", info->name);
+
+ info = maps.Get(3);
+ EXPECT_FALSE(info->flags & 0x8000);
+ EXPECT_EQ("/devsomething/does_not_exist", info->name);
+}
+
+TEST(MapsTest, file_smoke) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0 /fake.so\n"
+ "7b2b0000-7b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "7b2e0000-7b2f0000 r-xp c0000000 00:00 0 /fake3.so\n",
+ tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+
+ MapInfo* info = maps.Get(0);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x7b29b000U, info->start);
+ EXPECT_EQ(0x7b29e000U, info->end);
+ EXPECT_EQ(0xa0000000U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+ EXPECT_EQ("/fake.so", info->name);
+
+ info = maps.Get(1);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x7b2b0000U, info->start);
+ EXPECT_EQ(0x7b2e0000U, info->end);
+ EXPECT_EQ(0xb0000000U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+ EXPECT_EQ("/fake2.so", info->name);
+
+ info = maps.Get(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x7b2e0000U, info->start);
+ EXPECT_EQ(0x7b2f0000U, info->end);
+ EXPECT_EQ(0xc0000000U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+ EXPECT_EQ("/fake3.so", info->name);
+
+ ASSERT_TRUE(maps.Get(3) == nullptr);
+}
+
+TEST(MapsTest, file_no_map_name) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0\n"
+ "7b2b0000-7b2e0000 r-xp b0000000 00:00 0 /fake2.so\n"
+ "7b2e0000-7b2f0000 r-xp c0000000 00:00 0 \n",
+ tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(3U, maps.Total());
+
+ MapInfo* info = maps.Get(0);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x7b29b000U, info->start);
+ EXPECT_EQ(0x7b29e000U, info->end);
+ EXPECT_EQ(0xa0000000U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+ EXPECT_EQ("", info->name);
+
+ info = maps.Get(1);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x7b2b0000U, info->start);
+ EXPECT_EQ(0x7b2e0000U, info->end);
+ EXPECT_EQ(0xb0000000U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+ EXPECT_EQ("/fake2.so", info->name);
+
+ info = maps.Get(2);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x7b2e0000U, info->start);
+ EXPECT_EQ(0x7b2f0000U, info->end);
+ EXPECT_EQ(0xc0000000U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+ EXPECT_EQ("", info->name);
+
+ ASSERT_TRUE(maps.Get(3) == nullptr);
+}
+
+// Verify that a file that crosses a buffer is parsed correctly.
+static std::string CreateEntry(size_t index) {
+ return android::base::StringPrintf("%08zx-%08zx rwxp 0000 00:00 0\n", index * 4096,
+ (index + 1) * 4096);
+}
+
+TEST(MapsTest, file_buffer_cross) {
+ constexpr size_t kBufferSize = 2048;
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ // Compute how many to add in the first buffer.
+ size_t entry_len = CreateEntry(0).size();
+ size_t index;
+ std::string file_data;
+ for (index = 0; index < kBufferSize / entry_len; index++) {
+ file_data += CreateEntry(index);
+ }
+ // Add a long name to make sure that the first buffer does not contain a
+ // complete line.
+ // Remove the last newline.
+ size_t extra = 0;
+ size_t leftover = kBufferSize % entry_len;
+ size_t overlap1_index = 0;
+ std::string overlap1_name;
+ if (leftover == 0) {
+ // Exact match, add a long name to cross over the value.
+ overlap1_name = "/fake/name/is/long/on/purpose";
+ file_data.erase(file_data.size() - 1);
+ file_data += ' ' + overlap1_name + '\n';
+ extra = entry_len + overlap1_name.size() + 1;
+ overlap1_index = index;
+ }
+
+ // Compute how many need to go in to hit the buffer boundary exactly.
+ size_t bytes_left_in_buffer = kBufferSize - extra;
+ size_t entries_to_add = bytes_left_in_buffer / entry_len + index;
+ for (; index < entries_to_add; index++) {
+ file_data += CreateEntry(index);
+ }
+
+ // Now figure out how many bytes to add to get exactly to the buffer boundary.
+ leftover = bytes_left_in_buffer % entry_len;
+ std::string overlap2_name;
+ size_t overlap2_index = 0;
+ if (leftover != 0) {
+ file_data.erase(file_data.size() - 1);
+ file_data += ' ';
+ overlap2_name = std::string(leftover - 1, 'x');
+ file_data += overlap2_name + '\n';
+ overlap2_index = index - 1;
+ }
+
+ // Now add a few entries on the next page.
+ for (size_t start = index; index < start + 10; index++) {
+ file_data += CreateEntry(index);
+ }
+
+ ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+ ASSERT_TRUE(maps.Parse());
+ EXPECT_EQ(index, maps.Total());
+ // Verify all of the maps.
+ for (size_t i = 0; i < index; i++) {
+ MapInfo* info = maps.Get(i);
+ ASSERT_TRUE(info != nullptr) << "Failed verifying index " + std::to_string(i);
+ EXPECT_EQ(i * 4096, info->start) << "Failed verifying index " + std::to_string(i);
+ EXPECT_EQ((i + 1) * 4096, info->end) << "Failed verifying index " + std::to_string(i);
+ EXPECT_EQ(0U, info->offset) << "Failed verifying index " + std::to_string(i);
+ if (overlap1_index != 0 && i == overlap1_index) {
+ EXPECT_EQ(overlap1_name, info->name) << "Failed verifying overlap1 name " + std::to_string(i);
+ } else if (overlap2_index != 0 && i == overlap2_index) {
+ EXPECT_EQ(overlap2_name, info->name) << "Failed verifying overlap2 name " + std::to_string(i);
+ } else {
+ EXPECT_EQ("", info->name) << "Failed verifying index " + std::to_string(i);
+ }
+ }
+}
+
+TEST(MapsTest, file_should_fail) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n", tf.path,
+ 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_FALSE(maps.Parse());
+}
+
+// Create a maps file that is extremely large.
+TEST(MapsTest, large_file) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ std::string file_data;
+ uint64_t start = 0x700000;
+ for (size_t i = 0; i < 5000; i++) {
+ file_data +=
+ android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r-xp 1000 00:0 0 /fake%zu.so\n",
+ start + i * 4096, start + (i + 1) * 4096, i);
+ }
+
+ ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+ FileMaps maps(tf.path);
+
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5000U, maps.Total());
+ for (size_t i = 0; i < 5000; i++) {
+ MapInfo* info = maps.Get(i);
+ EXPECT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
+ EXPECT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
+ std::string name = "/fake" + std::to_string(i) + ".so";
+ EXPECT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
+ }
+}
+
+TEST(MapsTest, find) {
+ BufferMaps maps(
+ "1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n"
+ "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n"
+ "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n"
+ "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n"
+ "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n");
+ ASSERT_TRUE(maps.Parse());
+ ASSERT_EQ(5U, maps.Total());
+
+ EXPECT_TRUE(maps.Find(0x500) == nullptr);
+ EXPECT_TRUE(maps.Find(0x2000) == nullptr);
+ EXPECT_TRUE(maps.Find(0x5010) == nullptr);
+ EXPECT_TRUE(maps.Find(0x9a00) == nullptr);
+ EXPECT_TRUE(maps.Find(0xf000) == nullptr);
+ EXPECT_TRUE(maps.Find(0xf010) == nullptr);
+
+ MapInfo* info = maps.Find(0x1000);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x1000U, info->start);
+ EXPECT_EQ(0x2000U, info->end);
+ EXPECT_EQ(0x10U, info->offset);
+ EXPECT_EQ(PROT_READ, info->flags);
+ EXPECT_EQ("/system/lib/fake1.so", info->name);
+
+ info = maps.Find(0x3020);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x3000U, info->start);
+ EXPECT_EQ(0x4000U, info->end);
+ EXPECT_EQ(0x20U, info->offset);
+ EXPECT_EQ(PROT_WRITE, info->flags);
+ EXPECT_EQ("/system/lib/fake2.so", info->name);
+
+ info = maps.Find(0x6020);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0x6000U, info->start);
+ EXPECT_EQ(0x8000U, info->end);
+ EXPECT_EQ(0x30U, info->offset);
+ EXPECT_EQ(PROT_EXEC, info->flags);
+ EXPECT_EQ("/system/lib/fake3.so", info->name);
+
+ info = maps.Find(0xafff);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0xa000U, info->start);
+ EXPECT_EQ(0xb000U, info->end);
+ EXPECT_EQ(0x40U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+ EXPECT_EQ("/system/lib/fake4.so", info->name);
+
+ info = maps.Find(0xe500);
+ ASSERT_TRUE(info != nullptr);
+ EXPECT_EQ(0xe000U, info->start);
+ EXPECT_EQ(0xf000U, info->end);
+ EXPECT_EQ(0x50U, info->offset);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+ EXPECT_EQ("/system/lib/fake5.so", info->name);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
new file mode 100644
index 000000000..a6c12aab0
--- /dev/null
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "LogFake.h"
+#include "MemoryBuffer.h"
+
+namespace unwindstack {
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.reset(new MemoryBuffer);
+ }
+ std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+ ASSERT_EQ(0U, memory_->Size());
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1));
+ ASSERT_EQ(nullptr, memory_->GetPtr(0));
+ ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+ memory_->Resize(256);
+ ASSERT_EQ(256U, memory_->Size());
+ ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+ uint8_t* data = memory_->GetPtr(0);
+ for (size_t i = 0; i < memory_->Size(); i++) {
+ data[i] = i;
+ }
+
+ std::vector<uint8_t> buffer(memory_->Size());
+ ASSERT_TRUE(memory_->ReadFully(0, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+ ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 101));
+ ASSERT_FALSE(memory_->ReadFully(100, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(101, buffer.data(), 2));
+ ASSERT_FALSE(memory_->ReadFully(99, buffer.data(), 2));
+ ASSERT_TRUE(memory_->ReadFully(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+
+ ASSERT_FALSE(memory_->ReadFully(UINT64_MAX - 100, buffer.data(), 200));
+}
+
+TEST_F(MemoryBufferTest, Read) {
+ memory_->Resize(256);
+ ASSERT_EQ(256U, memory_->Size());
+ ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+ uint8_t* data = memory_->GetPtr(0);
+ for (size_t i = 0; i < memory_->Size(); i++) {
+ data[i] = i;
+ }
+
+ std::vector<uint8_t> buffer(memory_->Size());
+ ASSERT_EQ(128U, memory_->Read(128, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < 128; i++) {
+ ASSERT_EQ(128 + i, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
new file mode 100644
index 000000000..3bd3e4dbe
--- /dev/null
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryCache.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryCacheTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ memory_cache_.reset(new MemoryCache(memory_));
+
+ memory_->SetMemoryBlock(0x8000, 4096, 0xab);
+ memory_->SetMemoryBlock(0x9000, 4096, 0xde);
+ memory_->SetMemoryBlock(0xa000, 3000, 0x50);
+ }
+
+ MemoryFake* memory_;
+ std::unique_ptr<MemoryCache> memory_cache_;
+
+ constexpr static size_t kMaxCachedSize = 64;
+};
+
+TEST_F(MemoryCacheTest, cached_read) {
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, no_cached_read_after_clear) {
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is not used after a reset.
+ memory_cache_->Clear();
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, cached_read_across_caches) {
+ std::vector<uint8_t> expect(16, 0xab);
+ expect.resize(32, 0xde);
+
+ std::vector<uint8_t> buffer(32);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+
+ // Verify the cached data is used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ memory_->SetMemoryBlock(0x9000, 4096, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+}
+
+TEST_F(MemoryCacheTest, no_cache_read) {
+ for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is not used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail) {
+ std::vector<uint8_t> buffer(kMaxCachedSize);
+ ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+ ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
+
+ // Verify the cached data is not used.
+ memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+ ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail_cross) {
+ std::vector<uint8_t> expect(16, 0xde);
+ expect.resize(32, 0x50);
+
+ std::vector<uint8_t> buffer(32);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+
+ // Verify the cached data is not used for the second half but for the first.
+ memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+ expect.resize(16);
+ expect.resize(32, 0xff);
+ ASSERT_EQ(expect, buffer);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
new file mode 100644
index 000000000..5695dfcba
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto entry = data_.find(addr);
+ if (entry != data_.end()) {
+ entry->second = value;
+ } else {
+ data_.insert({addr, value});
+ }
+ }
+}
+
+void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto value = data_.find(addr);
+ if (value != data_.end()) {
+ value->second = src[i];
+ } else {
+ data_.insert({ addr, src[i] });
+ }
+ }
+}
+
+size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+ uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
+ for (size_t i = 0; i < size; i++, addr++) {
+ auto value = data_.find(addr);
+ if (value == data_.end()) {
+ return i;
+ }
+ dst[i] = value->second;
+ }
+ return size;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
new file mode 100644
index 000000000..20610a5e9
--- /dev/null
+++ b/libunwindstack/tests/MemoryFake.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryFake : public Memory {
+ public:
+ MemoryFake() = default;
+ virtual ~MemoryFake() = default;
+
+ size_t Read(uint64_t addr, void* buffer, size_t size) override;
+
+ void SetMemory(uint64_t addr, const void* memory, size_t length);
+
+ void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
+ void SetData8(uint64_t addr, uint8_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetData16(uint64_t addr, uint16_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetData32(uint64_t addr, uint32_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetData64(uint64_t addr, uint64_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
+ SetMemory(addr, values.data(), values.size());
+ }
+
+ void SetMemory(uint64_t addr, std::string string) {
+ SetMemory(addr, string.c_str(), string.size() + 1);
+ }
+
+ void Clear() { data_.clear(); }
+
+ private:
+ std::unordered_map<uint64_t, uint8_t> data_;
+};
+
+class MemoryFakeAlwaysReadZero : public Memory {
+ public:
+ MemoryFakeAlwaysReadZero() = default;
+ virtual ~MemoryFakeAlwaysReadZero() = default;
+
+ size_t Read(uint64_t, void* buffer, size_t size) override {
+ memset(buffer, 0, size);
+ return size;
+ }
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
new file mode 100644
index 000000000..4124a491c
--- /dev/null
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "MemoryFileAtOffset.h"
+
+namespace unwindstack {
+
+class MemoryFileTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ tf_ = new TemporaryFile;
+ }
+
+ void TearDown() override {
+ delete tf_;
+ }
+
+ void WriteTestData() {
+ ASSERT_TRUE(android::base::WriteStringToFd("0123456789abcdefghijklmnopqrstuvxyz", tf_->fd));
+ }
+
+ MemoryFileAtOffset memory_;
+
+ TemporaryFile* tf_ = nullptr;
+};
+
+TEST_F(MemoryFileTest, init_offset_0) {
+ WriteTestData();
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ ASSERT_STREQ("0123456789", buffer.data());
+}
+
+TEST_F(MemoryFileTest, init_offset_non_zero) {
+ WriteTestData();
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 10));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ ASSERT_STREQ("abcdefghij", buffer.data());
+}
+
+TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) {
+ size_t pagesize = getpagesize();
+ std::string large_string;
+ for (size_t i = 0; i < pagesize; i++) {
+ large_string += '1';
+ }
+ large_string += "012345678901234abcdefgh";
+ ASSERT_TRUE(android::base::WriteStringToFd(large_string, tf_->fd));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
+ std::vector<char> buffer(9);
+ ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 8));
+ buffer[8] = '\0';
+ ASSERT_STREQ("abcdefgh", buffer.data());
+}
+
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ for (size_t i = 0; i < 2 * pagesize; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ data += static_cast<char>((i % 10) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ std::string expected_str;
+ for (size_t i = 0; i < 5; i++) {
+ expected_str += '1';
+ expected_str += static_cast<char>(((i + pagesize) % 10) + '0');
+ }
+ ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ for (size_t i = 0; i < 2 * pagesize; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ data += static_cast<char>((i % 10) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
+ std::vector<char> buffer(11);
+ ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
+ buffer[10] = '\0';
+ std::string expected_str;
+ for (size_t i = 0; i < 5; i++) {
+ expected_str += '1';
+ expected_str += static_cast<char>(((i + pagesize + 5) % 10) + '0');
+ }
+ ASSERT_STREQ(expected_str.c_str(), buffer.data());
+}
+
+TEST_F(MemoryFileTest, init_offset_greater_than_filesize) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ uint64_t file_size = 2 * pagesize + pagesize / 2;
+ for (size_t i = 0; i < file_size; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ // Check offset > file size fails and aligned_offset > file size.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size + 2 * pagesize));
+ // Check offset == filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size));
+ // Check aligned_offset < filesize, but offset > filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, 2 * pagesize + pagesize / 2 + pagesize / 4));
+}
+
+TEST_F(MemoryFileTest, read_error) {
+ std::string data;
+ for (size_t i = 0; i < 5000; i++) {
+ data += static_cast<char>((i % 10) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ std::vector<char> buffer(100);
+
+ // Read before init.
+ ASSERT_FALSE(memory_.ReadFully(0, buffer.data(), 10));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+ ASSERT_FALSE(memory_.ReadFully(10000, buffer.data(), 10));
+ ASSERT_FALSE(memory_.ReadFully(5000, buffer.data(), 10));
+ ASSERT_FALSE(memory_.ReadFully(4990, buffer.data(), 11));
+ ASSERT_TRUE(memory_.ReadFully(4990, buffer.data(), 10));
+ ASSERT_FALSE(memory_.ReadFully(4999, buffer.data(), 2));
+ ASSERT_TRUE(memory_.ReadFully(4999, buffer.data(), 1));
+
+ // Check that overflow fails properly.
+ ASSERT_FALSE(memory_.ReadFully(UINT64_MAX - 100, buffer.data(), 200));
+}
+
+TEST_F(MemoryFileTest, read_past_file_within_mapping) {
+ size_t pagesize = getpagesize();
+
+ ASSERT_TRUE(pagesize > 100);
+ std::vector<uint8_t> buffer(pagesize - 100);
+ for (size_t i = 0; i < pagesize - 100; i++) {
+ buffer[i] = static_cast<uint8_t>((i % 0x5e) + 0x20);
+ }
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+
+ for (size_t i = 0; i < 100; i++) {
+ uint8_t value;
+ ASSERT_FALSE(memory_.ReadFully(buffer.size() + i, &value, 1))
+ << "Should have failed at value " << i;
+ }
+}
+
+TEST_F(MemoryFileTest, map_partial_offset_aligned) {
+ size_t pagesize = getpagesize();
+ std::vector<uint8_t> buffer(pagesize * 10);
+ for (size_t i = 0; i < pagesize * 10; i++) {
+ buffer[i] = i / pagesize + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+ // Map in only two pages of the data, and after the first page.
+ ASSERT_TRUE(memory_.Init(tf_->path, pagesize, pagesize * 2));
+
+ std::vector<uint8_t> read_buffer(pagesize * 2);
+ // Make sure that reading after mapped data is a failure.
+ ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1));
+ ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2));
+ for (size_t i = 0; i < pagesize; i++) {
+ ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
+ }
+ for (size_t i = pagesize; i < pagesize * 2; i++) {
+ ASSERT_EQ(3, read_buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryFileTest, map_partial_offset_unaligned) {
+ size_t pagesize = getpagesize();
+ ASSERT_TRUE(pagesize > 0x100);
+ std::vector<uint8_t> buffer(pagesize * 10);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i / pagesize + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+ // Map in only two pages of the data, and after the first page.
+ ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, pagesize * 2));
+
+ std::vector<uint8_t> read_buffer(pagesize * 2);
+ // Make sure that reading after mapped data is a failure.
+ ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1));
+ ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2));
+ for (size_t i = 0; i < pagesize - 0x100; i++) {
+ ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
+ }
+ for (size_t i = pagesize - 0x100; i < 2 * pagesize - 0x100; i++) {
+ ASSERT_EQ(3, read_buffer[i]) << "Failed at byte " << i;
+ }
+ for (size_t i = 2 * pagesize - 0x100; i < pagesize * 2; i++) {
+ ASSERT_EQ(4, read_buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryFileTest, map_overflow) {
+ size_t pagesize = getpagesize();
+ ASSERT_TRUE(pagesize > 0x100);
+ std::vector<uint8_t> buffer(pagesize * 10);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i / pagesize + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+ // Map in only two pages of the data, and after the first page.
+ ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, UINT64_MAX));
+
+ std::vector<uint8_t> read_buffer(pagesize * 10);
+ ASSERT_FALSE(memory_.ReadFully(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1));
+ ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 9 - 0x100));
+}
+
+TEST_F(MemoryFileTest, init_reinit) {
+ size_t pagesize = getpagesize();
+ std::vector<uint8_t> buffer(pagesize * 2);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i / pagesize + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
+
+ ASSERT_TRUE(memory_.Init(tf_->path, 0));
+ std::vector<uint8_t> read_buffer(buffer.size());
+ ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize));
+ for (size_t i = 0; i < pagesize; i++) {
+ ASSERT_EQ(1, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ // Now reinit.
+ ASSERT_TRUE(memory_.Init(tf_->path, pagesize));
+ ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize));
+ for (size_t i = 0; i < pagesize; i++) {
+ ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
new file mode 100644
index 000000000..c9e5dc0fd
--- /dev/null
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryLocal.h"
+
+namespace unwindstack {
+
+TEST(MemoryLocalTest, read) {
+ std::vector<uint8_t> src(1024);
+ memset(src.data(), 0x4c, 1024);
+
+ MemoryLocal local;
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(local.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+ ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]);
+ }
+
+ memset(src.data(), 0x23, 512);
+ ASSERT_TRUE(local.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+ ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
+ for (size_t i = 0; i < 512; i++) {
+ ASSERT_EQ(0x23U, dst[i]);
+ }
+ for (size_t i = 512; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]);
+ }
+}
+
+TEST(MemoryLocalTest, read_illegal) {
+ MemoryLocal local;
+
+ std::vector<uint8_t> dst(100);
+ ASSERT_FALSE(local.ReadFully(0, dst.data(), 1));
+ ASSERT_FALSE(local.ReadFully(0, dst.data(), 100));
+}
+
+TEST(MemoryLocalTest, read_overflow) {
+ MemoryLocal local;
+
+ // On 32 bit this test doesn't necessarily cause an overflow. The 64 bit
+ // version will always go through the overflow check.
+ std::vector<uint8_t> dst(100);
+ uint64_t value;
+ ASSERT_FALSE(local.ReadFully(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
+
+TEST(MemoryLocalTest, Read) {
+ char* mapping = static_cast<char*>(
+ mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+
+ ASSERT_NE(MAP_FAILED, mapping);
+
+ mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE);
+ memset(mapping + getpagesize() - 1024, 0x4c, 1024);
+
+ MemoryLocal local;
+
+ std::vector<uint8_t> dst(4096);
+ ASSERT_EQ(1024U, local.Read(reinterpret_cast<uint64_t>(mapping + getpagesize() - 1024),
+ dst.data(), 4096));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_EQ(0, munmap(mapping, 2 * getpagesize()));
+}
+
+TEST(MemoryLocalTest, read_hole) {
+ void* mapping =
+ mmap(nullptr, 3 * 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, mapping);
+ memset(mapping, 0xFF, 3 * 4096);
+ mprotect(static_cast<char*>(mapping) + 4096, 4096, PROT_NONE);
+
+ MemoryLocal local;
+ std::vector<uint8_t> dst(4096 * 3, 0xCC);
+ ASSERT_EQ(4096U, local.Read(reinterpret_cast<uintptr_t>(mapping), dst.data(), 4096 * 3));
+ for (size_t i = 0; i < 4096; ++i) {
+ ASSERT_EQ(0xFF, dst[i]);
+ }
+ for (size_t i = 4096; i < 4096 * 3; ++i) {
+ ASSERT_EQ(0xCC, dst[i]);
+ }
+ ASSERT_EQ(0, munmap(mapping, 3 * 4096));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
new file mode 100644
index 000000000..953170848
--- /dev/null
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "LogFake.h"
+#include "MemoryOfflineBuffer.h"
+
+namespace unwindstack {
+
+class MemoryOfflineBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
+ }
+
+ static void SetUpTestSuite() {
+ buffer_.resize(kLength);
+ for (size_t i = 0; i < kLength; i++) {
+ buffer_[i] = i % 189;
+ }
+ }
+
+ std::unique_ptr<MemoryOfflineBuffer> memory_;
+
+ static constexpr size_t kLength = 0x2000;
+ static constexpr uint64_t kStart = 0x1000;
+ static constexpr uint64_t kEnd = kStart + kLength;
+ static std::vector<uint8_t> buffer_;
+};
+
+std::vector<uint8_t> MemoryOfflineBufferTest::buffer_;
+
+static void VerifyBuffer(uint8_t* buffer, size_t start_value, size_t length) {
+ for (size_t i = 0; i < length; i++) {
+ ASSERT_EQ((start_value + i) % 189, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryOfflineBufferTest, read_out_of_bounds) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 2));
+ ASSERT_FALSE(memory_->ReadFully(0x3000, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(0x3001, buffer.data(), 1));
+}
+
+TEST_F(MemoryOfflineBufferTest, read) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 10));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 10));
+
+ ASSERT_TRUE(memory_->ReadFully(kStart + 555, buffer.data(), 40));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 555, 40));
+
+ ASSERT_TRUE(memory_->ReadFully(kStart + kLength - 105, buffer.data(), 105));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), kLength - 105, 105));
+}
+
+TEST_F(MemoryOfflineBufferTest, read_past_end) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_EQ(100U, memory_->Read(kStart + kLength - 100, buffer.data(), buffer.size()));
+ VerifyBuffer(buffer.data(), kLength - 100, 100);
+}
+
+TEST_F(MemoryOfflineBufferTest, read_after_reset) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 100));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 100));
+
+ memory_->Reset(&buffer_[10], 0x12000, 0x13000);
+ ASSERT_TRUE(memory_->ReadFully(0x12000, buffer.data(), 100));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 10, 100));
+
+ ASSERT_EQ(50U, memory_->Read(0x13000 - 50, buffer.data(), buffer.size()));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0x1000 - 50 + 10, 50));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
new file mode 100644
index 000000000..d0c441ba5
--- /dev/null
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+
+#include "MemoryOffline.h"
+
+namespace unwindstack {
+
+class MemoryOfflineTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ for (size_t i = 0; i < 1024; ++i) {
+ data.push_back(i & 0xff);
+ }
+
+ ASSERT_TRUE(android::base::WriteFully(temp_file.fd, &offset, sizeof(offset)));
+ ASSERT_TRUE(android::base::WriteFully(temp_file.fd, data.data(), data.size()));
+
+ memory = std::make_unique<MemoryOffline>();
+ ASSERT_TRUE(memory != nullptr);
+
+ ASSERT_TRUE(memory->Init(temp_file.path, 0));
+ }
+
+ TemporaryFile temp_file;
+ uint64_t offset = 4096;
+ std::vector<char> data;
+ std::unique_ptr<MemoryOffline> memory;
+};
+
+TEST_F(MemoryOfflineTest, read_boundaries) {
+ char buf = '\0';
+ ASSERT_EQ(0U, memory->Read(offset - 1, &buf, 1));
+ ASSERT_EQ(0U, memory->Read(offset + data.size(), &buf, 1));
+ ASSERT_EQ(1U, memory->Read(offset, &buf, 1));
+ ASSERT_EQ(buf, data.front());
+ ASSERT_EQ(1U, memory->Read(offset + data.size() - 1, &buf, 1));
+ ASSERT_EQ(buf, data.back());
+}
+
+TEST_F(MemoryOfflineTest, read_values) {
+ std::vector<char> buf;
+ buf.resize(2 * data.size());
+ ASSERT_EQ(data.size(), memory->Read(offset, buf.data(), buf.size()));
+ buf.resize(data.size());
+ ASSERT_EQ(buf, data);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
new file mode 100644
index 000000000..2d4f141ab
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryFake.h"
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ process_memory_.reset();
+ memory_fake_ = new MemoryFake;
+ process_memory_.reset(memory_fake_);
+ }
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+ memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
+
+ MemoryRange range(process_memory_, 9001, 1024, 0);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+ memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
+
+ MemoryRange range(process_memory_, 1000, 1024, 0);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
+ for (size_t i = 0; i < 4; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ // Verify that reads outside of the range will fail.
+ ASSERT_FALSE(range.ReadFully(1020, dst.data(), 5));
+ ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1));
+ ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1024));
+
+ // Verify that reading up to the end works.
+ ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
+}
+
+TEST_F(MemoryRangeTest, read_fully_overflow) {
+ std::vector<uint8_t> buffer(100);
+
+ std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
+ std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200, 0));
+ ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
+}
+
+TEST_F(MemoryRangeTest, read) {
+ memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
+
+ MemoryRange range(process_memory_, 1000, 1024, 0);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
+ for (size_t i = 0; i < 4; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+ memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+ MemoryRange range(process_memory_, 1000, 1024, 400);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
+ ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 000000000..e4e9fc4e9
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryFake.h"
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ MemoryFake* memory = new MemoryFake;
+ process_memory_.reset(memory);
+ memory->SetMemoryBlock(1000, 5000, 0x15);
+ memory->SetMemoryBlock(6000, 12000, 0x26);
+ memory->SetMemoryBlock(14000, 20000, 0x37);
+ memory->SetMemoryBlock(20000, 22000, 0x48);
+
+ ranges_.reset(new MemoryRanges);
+ ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+ ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+ ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+ ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+ ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+ }
+
+ std::shared_ptr<Memory> process_memory_;
+ std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+ std::vector<uint8_t> dst(2000);
+ size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(2000, dst.data(), dst.size());
+ ASSERT_EQ(2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(4000, dst.data(), dst.size());
+ ASSERT_EQ(100UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+ std::vector<uint8_t> dst(4096);
+ ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+ // The MemoryRanges object does not support reading across a range,
+ // so this will only read in the first range.
+ std::vector<uint8_t> dst(4096);
+ size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
new file mode 100644
index 000000000..c90dedcec
--- /dev/null
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "MemoryRemote.h"
+
+#include "MemoryFake.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+class MemoryRemoteTest : public ::testing::Test {
+ protected:
+ static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ return TestQuiescePid(pid);
+ }
+
+ static bool Detach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+ }
+
+ static constexpr size_t NS_PER_SEC = 1000000000ULL;
+};
+
+TEST_F(MemoryRemoteTest, read) {
+ std::vector<uint8_t> src(1024);
+ memset(src.data(), 0x4c, 1024);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_large) {
+ static constexpr size_t kTotalPages = 245;
+ std::vector<uint8_t> src(kTotalPages * getpagesize());
+ for (size_t i = 0; i < kTotalPages; i++) {
+ memset(&src[i * getpagesize()], i, getpagesize());
+ }
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(kTotalPages * getpagesize());
+ ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), src.size()));
+ for (size_t i = 0; i < kTotalPages * getpagesize(); i++) {
+ ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_partial) {
+ char* mapping = static_cast<char*>(
+ mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ ASSERT_NE(MAP_FAILED, mapping);
+ memset(mapping, 0x4c, 4 * getpagesize());
+ ASSERT_EQ(0, mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE));
+ ASSERT_EQ(0, munmap(mapping + 3 * getpagesize(), getpagesize()));
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ // Unmap from our process.
+ ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(4096);
+ size_t bytes =
+ remote.Read(reinterpret_cast<uint64_t>(mapping + getpagesize() - 1024), dst.data(), 4096);
+ // Some read methods can read PROT_NONE maps, allow that.
+ ASSERT_LE(1024U, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ // Now verify that reading stops at the end of a map.
+ bytes =
+ remote.Read(reinterpret_cast<uint64_t>(mapping + 3 * getpagesize() - 1024), dst.data(), 4096);
+ ASSERT_EQ(1024U, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_fail) {
+ int pagesize = getpagesize();
+ void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
+ memset(src, 0x4c, pagesize * 2);
+ ASSERT_NE(MAP_FAILED, src);
+ // Put a hole right after the first page.
+ ASSERT_EQ(0, munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(src) + pagesize),
+ pagesize));
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(pagesize);
+ ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
+ for (size_t i = 0; i < 1024; i++) {
+ ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
+ ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
+ ASSERT_FALSE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+
+ // Check overflow condition is caught properly.
+ ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
+
+ ASSERT_EQ(0, munmap(src, pagesize));
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_overflow) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ // Check overflow condition is caught properly.
+ std::vector<uint8_t> dst(200);
+ ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_illegal) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+
+ std::vector<uint8_t> dst(100);
+ ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
+ ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+ size_t page_size = getpagesize();
+ void* mapping =
+ mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, mapping);
+ memset(mapping, 0xFF, 3 * page_size);
+ ASSERT_EQ(0, mprotect(static_cast<char*>(mapping) + page_size, page_size, PROT_NONE));
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true);
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_EQ(0, munmap(mapping, 3 * page_size));
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+ std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
+ size_t read_size = remote.Read(reinterpret_cast<uint64_t>(mapping), dst.data(), page_size * 3);
+ // Some read methods can read PROT_NONE maps, allow that.
+ ASSERT_LE(page_size, read_size);
+ for (size_t i = 0; i < read_size; ++i) {
+ ASSERT_EQ(0xFF, dst[i]);
+ }
+ for (size_t i = read_size; i < dst.size(); ++i) {
+ ASSERT_EQ(0xCC, dst[i]);
+ }
+}
+
+TEST_F(MemoryRemoteTest, read_munmap_hole) {
+ size_t page_size = getpagesize();
+ void* mapping =
+ mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, mapping);
+ memset(mapping, 0xFF, 3 * page_size);
+ ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + page_size, page_size));
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_EQ(0, munmap(mapping, page_size));
+ ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
+
+ ASSERT_TRUE(Attach(pid));
+
+ MemoryRemote remote(pid);
+ std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
+ size_t read_size = remote.Read(reinterpret_cast<uint64_t>(mapping), dst.data(), page_size * 3);
+ ASSERT_EQ(page_size, read_size);
+ for (size_t i = 0; i < read_size; ++i) {
+ ASSERT_EQ(0xFF, dst[i]);
+ }
+ for (size_t i = read_size; i < dst.size(); ++i) {
+ ASSERT_EQ(0xCC, dst[i]);
+ }
+}
+
+// Verify that the memory remote object chooses a memory read function
+// properly. Either process_vm_readv or ptrace.
+TEST_F(MemoryRemoteTest, read_choose_correctly) {
+ size_t page_size = getpagesize();
+ void* mapping =
+ mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, mapping);
+ memset(mapping, 0xFC, 2 * page_size);
+ ASSERT_EQ(0, mprotect(static_cast<char*>(mapping), page_size, PROT_NONE));
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_EQ(0, munmap(mapping, 2 * page_size));
+
+ ASSERT_TRUE(Attach(pid));
+
+ // We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
+ // Read from the PROT_NONE area first to force the choice of ptrace.
+ MemoryRemote remote_ptrace(pid);
+ uint32_t value;
+ size_t bytes = remote_ptrace.Read(reinterpret_cast<uint64_t>(mapping), &value, sizeof(value));
+ ASSERT_EQ(sizeof(value), bytes);
+ ASSERT_EQ(0xfcfcfcfcU, value);
+ bytes = remote_ptrace.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
+ ASSERT_EQ(sizeof(value), bytes);
+ ASSERT_EQ(0xfcfcfcfcU, value);
+ bytes = remote_ptrace.Read(reinterpret_cast<uint64_t>(mapping), &value, sizeof(value));
+ ASSERT_EQ(sizeof(value), bytes);
+ ASSERT_EQ(0xfcfcfcfcU, value);
+
+ // Now verify that choosing process_vm_readv results in failing reads of
+ // the PROT_NONE part of the map. Read from a valid map first which
+ // should prefer process_vm_readv, and keep that as the read function.
+ MemoryRemote remote_readv(pid);
+ bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
+ ASSERT_EQ(sizeof(value), bytes);
+ ASSERT_EQ(0xfcfcfcfcU, value);
+ bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping), &value, sizeof(value));
+ ASSERT_EQ(0U, bytes);
+ bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
+ ASSERT_EQ(sizeof(value), bytes);
+ ASSERT_EQ(0xfcfcfcfcU, value);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 000000000..365598499
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+TEST(MemoryTest, read32) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint32_t data = 0xffffffff;
+ ASSERT_TRUE(memory.Read32(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+TEST(MemoryTest, read64) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint64_t data = 0xffffffffffffffffULL;
+ ASSERT_TRUE(memory.Read64(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+struct FakeStruct {
+ int one;
+ bool two;
+ uint32_t three;
+ uint64_t four;
+};
+
+TEST(MemoryTest, read_string) {
+ std::string name("string_in_memory");
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, name.c_str(), name.size() + 1);
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_EQ("string_in_memory", dst_name);
+
+ ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_EQ("in_memory", dst_name);
+
+ // Set size greater than string.
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 10));
+ ASSERT_EQ("in_memory", dst_name);
+
+ ASSERT_FALSE(memory.ReadString(107, &dst_name, 9));
+}
+
+TEST(MemoryTest, read_string_error) {
+ std::string name("short");
+
+ MemoryFake memory;
+
+ // Save everything except the terminating '\0'.
+ memory.SetMemory(0, name.c_str(), name.size());
+
+ std::string dst_name;
+ // Read from a non-existant address.
+ ASSERT_FALSE(memory.ReadString(100, &dst_name));
+
+ // This should fail because there is no terminating '\0'.
+ ASSERT_FALSE(memory.ReadString(0, &dst_name));
+
+ // This should pass because there is a terminating '\0'.
+ memory.SetData8(name.size(), '\0');
+ ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_EQ("short", dst_name);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
new file mode 100644
index 000000000..207d46efe
--- /dev/null
+++ b/libunwindstack/tests/RegsFake.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+
+#include <stdint.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "Check.h"
+
+namespace unwindstack {
+
+class RegsFake : public Regs {
+ public:
+ RegsFake(uint16_t total_regs) : Regs(total_regs, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsFake() = default;
+
+ ArchEnum Arch() override { return fake_arch_; }
+ void* RawData() override { return nullptr; }
+ uint64_t pc() override { return fake_pc_; }
+ uint64_t sp() override { return fake_sp_; }
+ void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+ void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+
+ bool SetPcFromReturnAddress(Memory*) override {
+ if (!fake_return_address_valid_) {
+ return false;
+ }
+ fake_pc_ = fake_return_address_;
+ return true;
+ }
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
+
+ bool Is32Bit() {
+ CHECK(fake_arch_ != ARCH_UNKNOWN);
+ return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS;
+ }
+
+ uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
+
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+ void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
+ void FakeSetDexPc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
+ void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
+ void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
+
+ Regs* Clone() override { return nullptr; }
+
+ private:
+ ArchEnum fake_arch_ = ARCH_UNKNOWN;
+ uint64_t fake_pc_ = 0;
+ uint64_t fake_sp_ = 0;
+ bool fake_return_address_valid_ = false;
+ uint64_t fake_return_address_ = 0;
+};
+
+template <typename TypeParam>
+class RegsImplFake : public RegsImpl<TypeParam> {
+ public:
+ RegsImplFake(uint16_t total_regs)
+ : RegsImpl<TypeParam>(total_regs, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsImplFake() = default;
+
+ ArchEnum Arch() override { return ARCH_UNKNOWN; }
+ uint64_t pc() override { return fake_pc_; }
+ uint64_t sp() override { return fake_sp_; }
+ void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+ void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+
+ uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 0; }
+ bool SetPcFromReturnAddress(Memory*) override { return false; }
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+ Regs* Clone() override { return nullptr; }
+
+ private:
+ uint64_t fake_pc_ = 0;
+ uint64_t fake_sp_ = 0;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsInfoTest.cpp b/libunwindstack/tests/RegsInfoTest.cpp
new file mode 100644
index 000000000..a6bc2c5ae
--- /dev/null
+++ b/libunwindstack/tests/RegsInfoTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Regs.h>
+
+#include "RegsFake.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
+
+TEST(RegsInfoTest, single_uint32_t) {
+ RegsImplFake<uint32_t> regs(10);
+ RegsInfo<uint32_t> info(&regs);
+
+ regs[1] = 0x100;
+ ASSERT_FALSE(info.IsSaved(1));
+ ASSERT_EQ(0x100U, info.Get(1));
+ ASSERT_EQ(10, info.Total());
+
+ uint32_t* value = info.Save(1);
+ ASSERT_EQ(value, &regs[1]);
+ regs[1] = 0x200;
+ ASSERT_TRUE(info.IsSaved(1));
+ ASSERT_EQ(0x100U, info.Get(1));
+ ASSERT_EQ(0x200U, regs[1]);
+}
+
+TEST(RegsInfoTest, single_uint64_t) {
+ RegsImplFake<uint64_t> regs(20);
+ RegsInfo<uint64_t> info(&regs);
+
+ regs[3] = 0x300;
+ ASSERT_FALSE(info.IsSaved(3));
+ ASSERT_EQ(0x300U, info.Get(3));
+ ASSERT_EQ(20, info.Total());
+
+ uint64_t* value = info.Save(3);
+ ASSERT_EQ(value, &regs[3]);
+ regs[3] = 0x400;
+ ASSERT_TRUE(info.IsSaved(3));
+ ASSERT_EQ(0x300U, info.Get(3));
+ ASSERT_EQ(0x400U, regs[3]);
+}
+
+TEST(RegsInfoTest, all) {
+ RegsImplFake<uint64_t> regs(64);
+ RegsInfo<uint64_t> info(&regs);
+
+ for (uint32_t i = 0; i < 64; i++) {
+ regs[i] = i * 0x100;
+ ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+ }
+
+ for (uint32_t i = 0; i < 64; i++) {
+ ASSERT_FALSE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+ uint64_t* reg = info.Save(i);
+ ASSERT_EQ(reg, &regs[i]) << "Reg " + std::to_string(i) + " failed.";
+ *reg = i * 0x1000 + 0x100;
+ ASSERT_EQ(i * 0x1000 + 0x100, regs[i]) << "Reg " + std::to_string(i) + " failed.";
+ }
+
+ for (uint32_t i = 0; i < 64; i++) {
+ ASSERT_TRUE(info.IsSaved(i)) << "Reg " + std::to_string(i) + " failed.";
+ ASSERT_EQ(i * 0x100, info.Get(i)) << "Reg " + std::to_string(i) + " failed.";
+ }
+}
+
+TEST(RegsInfoTest, invalid_register) {
+ RegsImplFake<uint64_t> regs(64);
+ RegsInfo<uint64_t> info(&regs);
+
+ EXPECT_DEATH(info.Save(RegsInfo<uint64_t>::MAX_REGISTERS), "");
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
new file mode 100644
index 000000000..47e605a82
--- /dev/null
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <utility>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+namespace unwindstack {
+
+struct Register {
+ std::string expected_name;
+ uint64_t offset;
+
+ bool operator==(const Register& rhs) const {
+ return std::tie(expected_name, offset) == std::tie(rhs.expected_name, rhs.offset);
+ }
+};
+
+template<typename T>
+class RegsIterateTest : public ::testing::Test {
+};
+
+template<typename RegsType>
+std::vector<Register> ExpectedRegisters();
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsArm>() {
+ std::vector<Register> result;
+ result.push_back({"r0", ARM_REG_R0});
+ result.push_back({"r1", ARM_REG_R1});
+ result.push_back({"r2", ARM_REG_R2});
+ result.push_back({"r3", ARM_REG_R3});
+ result.push_back({"r4", ARM_REG_R4});
+ result.push_back({"r5", ARM_REG_R5});
+ result.push_back({"r6", ARM_REG_R6});
+ result.push_back({"r7", ARM_REG_R7});
+ result.push_back({"r8", ARM_REG_R8});
+ result.push_back({"r9", ARM_REG_R9});
+ result.push_back({"r10", ARM_REG_R10});
+ result.push_back({"r11", ARM_REG_R11});
+ result.push_back({"ip", ARM_REG_R12});
+ result.push_back({"sp", ARM_REG_SP});
+ result.push_back({"lr", ARM_REG_LR});
+ result.push_back({"pc", ARM_REG_PC});
+ return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsArm64>() {
+ std::vector<Register> result;
+ result.push_back({"x0", ARM64_REG_R0});
+ result.push_back({"x1", ARM64_REG_R1});
+ result.push_back({"x2", ARM64_REG_R2});
+ result.push_back({"x3", ARM64_REG_R3});
+ result.push_back({"x4", ARM64_REG_R4});
+ result.push_back({"x5", ARM64_REG_R5});
+ result.push_back({"x6", ARM64_REG_R6});
+ result.push_back({"x7", ARM64_REG_R7});
+ result.push_back({"x8", ARM64_REG_R8});
+ result.push_back({"x9", ARM64_REG_R9});
+ result.push_back({"x10", ARM64_REG_R10});
+ result.push_back({"x11", ARM64_REG_R11});
+ result.push_back({"x12", ARM64_REG_R12});
+ result.push_back({"x13", ARM64_REG_R13});
+ result.push_back({"x14", ARM64_REG_R14});
+ result.push_back({"x15", ARM64_REG_R15});
+ result.push_back({"x16", ARM64_REG_R16});
+ result.push_back({"x17", ARM64_REG_R17});
+ result.push_back({"x18", ARM64_REG_R18});
+ result.push_back({"x19", ARM64_REG_R19});
+ result.push_back({"x20", ARM64_REG_R20});
+ result.push_back({"x21", ARM64_REG_R21});
+ result.push_back({"x22", ARM64_REG_R22});
+ result.push_back({"x23", ARM64_REG_R23});
+ result.push_back({"x24", ARM64_REG_R24});
+ result.push_back({"x25", ARM64_REG_R25});
+ result.push_back({"x26", ARM64_REG_R26});
+ result.push_back({"x27", ARM64_REG_R27});
+ result.push_back({"x28", ARM64_REG_R28});
+ result.push_back({"x29", ARM64_REG_R29});
+ result.push_back({"lr", ARM64_REG_LR});
+ result.push_back({"sp", ARM64_REG_SP});
+ result.push_back({"pc", ARM64_REG_PC});
+ result.push_back({"pst", ARM64_REG_PSTATE});
+ return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsX86>() {
+ std::vector<Register> result;
+ result.push_back({"eax", X86_REG_EAX});
+ result.push_back({"ebx", X86_REG_EBX});
+ result.push_back({"ecx", X86_REG_ECX});
+ result.push_back({"edx", X86_REG_EDX});
+ result.push_back({"ebp", X86_REG_EBP});
+ result.push_back({"edi", X86_REG_EDI});
+ result.push_back({"esi", X86_REG_ESI});
+ result.push_back({"esp", X86_REG_ESP});
+ result.push_back({"eip", X86_REG_EIP});
+ return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsX86_64>() {
+ std::vector<Register> result;
+ result.push_back({"rax", X86_64_REG_RAX});
+ result.push_back({"rbx", X86_64_REG_RBX});
+ result.push_back({"rcx", X86_64_REG_RCX});
+ result.push_back({"rdx", X86_64_REG_RDX});
+ result.push_back({"r8", X86_64_REG_R8});
+ result.push_back({"r9", X86_64_REG_R9});
+ result.push_back({"r10", X86_64_REG_R10});
+ result.push_back({"r11", X86_64_REG_R11});
+ result.push_back({"r12", X86_64_REG_R12});
+ result.push_back({"r13", X86_64_REG_R13});
+ result.push_back({"r14", X86_64_REG_R14});
+ result.push_back({"r15", X86_64_REG_R15});
+ result.push_back({"rdi", X86_64_REG_RDI});
+ result.push_back({"rsi", X86_64_REG_RSI});
+ result.push_back({"rbp", X86_64_REG_RBP});
+ result.push_back({"rsp", X86_64_REG_RSP});
+ result.push_back({"rip", X86_64_REG_RIP});
+ return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips>() {
+ std::vector<Register> result;
+ result.push_back({"r0", MIPS_REG_R0});
+ result.push_back({"r1", MIPS_REG_R1});
+ result.push_back({"r2", MIPS_REG_R2});
+ result.push_back({"r3", MIPS_REG_R3});
+ result.push_back({"r4", MIPS_REG_R4});
+ result.push_back({"r5", MIPS_REG_R5});
+ result.push_back({"r6", MIPS_REG_R6});
+ result.push_back({"r7", MIPS_REG_R7});
+ result.push_back({"r8", MIPS_REG_R8});
+ result.push_back({"r9", MIPS_REG_R9});
+ result.push_back({"r10", MIPS_REG_R10});
+ result.push_back({"r11", MIPS_REG_R11});
+ result.push_back({"r12", MIPS_REG_R12});
+ result.push_back({"r13", MIPS_REG_R13});
+ result.push_back({"r14", MIPS_REG_R14});
+ result.push_back({"r15", MIPS_REG_R15});
+ result.push_back({"r16", MIPS_REG_R16});
+ result.push_back({"r17", MIPS_REG_R17});
+ result.push_back({"r18", MIPS_REG_R18});
+ result.push_back({"r19", MIPS_REG_R19});
+ result.push_back({"r20", MIPS_REG_R20});
+ result.push_back({"r21", MIPS_REG_R21});
+ result.push_back({"r22", MIPS_REG_R22});
+ result.push_back({"r23", MIPS_REG_R23});
+ result.push_back({"r24", MIPS_REG_R24});
+ result.push_back({"r25", MIPS_REG_R25});
+ result.push_back({"r26", MIPS_REG_R26});
+ result.push_back({"r27", MIPS_REG_R27});
+ result.push_back({"r28", MIPS_REG_R28});
+ result.push_back({"sp", MIPS_REG_SP});
+ result.push_back({"r30", MIPS_REG_R30});
+ result.push_back({"ra", MIPS_REG_RA});
+ result.push_back({"pc", MIPS_REG_PC});
+
+ return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips64>() {
+ std::vector<Register> result;
+ result.push_back({"r0", MIPS64_REG_R0});
+ result.push_back({"r1", MIPS64_REG_R1});
+ result.push_back({"r2", MIPS64_REG_R2});
+ result.push_back({"r3", MIPS64_REG_R3});
+ result.push_back({"r4", MIPS64_REG_R4});
+ result.push_back({"r5", MIPS64_REG_R5});
+ result.push_back({"r6", MIPS64_REG_R6});
+ result.push_back({"r7", MIPS64_REG_R7});
+ result.push_back({"r8", MIPS64_REG_R8});
+ result.push_back({"r9", MIPS64_REG_R9});
+ result.push_back({"r10", MIPS64_REG_R10});
+ result.push_back({"r11", MIPS64_REG_R11});
+ result.push_back({"r12", MIPS64_REG_R12});
+ result.push_back({"r13", MIPS64_REG_R13});
+ result.push_back({"r14", MIPS64_REG_R14});
+ result.push_back({"r15", MIPS64_REG_R15});
+ result.push_back({"r16", MIPS64_REG_R16});
+ result.push_back({"r17", MIPS64_REG_R17});
+ result.push_back({"r18", MIPS64_REG_R18});
+ result.push_back({"r19", MIPS64_REG_R19});
+ result.push_back({"r20", MIPS64_REG_R20});
+ result.push_back({"r21", MIPS64_REG_R21});
+ result.push_back({"r22", MIPS64_REG_R22});
+ result.push_back({"r23", MIPS64_REG_R23});
+ result.push_back({"r24", MIPS64_REG_R24});
+ result.push_back({"r25", MIPS64_REG_R25});
+ result.push_back({"r26", MIPS64_REG_R26});
+ result.push_back({"r27", MIPS64_REG_R27});
+ result.push_back({"r28", MIPS64_REG_R28});
+ result.push_back({"sp", MIPS64_REG_SP});
+ result.push_back({"r30", MIPS64_REG_R30});
+ result.push_back({"ra", MIPS64_REG_RA});
+ result.push_back({"pc", MIPS64_REG_PC});
+
+ return result;
+}
+
+using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
+TYPED_TEST_SUITE(RegsIterateTest, RegTypes);
+
+TYPED_TEST(RegsIterateTest, iterate) {
+ std::vector<Register> expected = ExpectedRegisters<TypeParam>();
+ TypeParam regs;
+ for (const auto& reg : expected) {
+ regs[reg.offset] = reg.offset;
+ }
+
+ std::vector<Register> actual;
+ regs.IterateRegisters([&actual](const char* name, uint64_t value) {
+ actual.push_back({name, value});
+ });
+
+ ASSERT_EQ(expected, actual);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
new file mode 100644
index 000000000..eac12ca64
--- /dev/null
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class RegsStepIfSignalHandlerTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ elf_memory_ = new MemoryFake;
+ elf_.reset(new Elf(elf_memory_));
+ }
+
+ void ArmStepIfSignalHandlerNonRt(uint32_t pc_data);
+ void ArmStepIfSignalHandlerRt(uint32_t pc_data);
+
+ MemoryFake* elf_memory_;
+ MemoryFake process_memory_;
+ std::unique_ptr<Elf> elf_;
+};
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) {
+ uint64_t addr = 0x1000;
+ RegsArm regs;
+ regs[ARM_REG_PC] = 0x5000;
+ regs[ARM_REG_SP] = addr;
+
+ elf_memory_->SetData32(0x5000, pc_data);
+
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x100U, regs[ARM_REG_SP]);
+ EXPECT_EQ(0x120U, regs[ARM_REG_PC]);
+ EXPECT_EQ(0x100U, regs.sp());
+ EXPECT_EQ(0x120U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_non_rt) {
+ // Form 1
+ ArmStepIfSignalHandlerNonRt(0xe3a07077);
+
+ // Form 2
+ ArmStepIfSignalHandlerNonRt(0xef900077);
+
+ // Form 3
+ ArmStepIfSignalHandlerNonRt(0xdf002777);
+}
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) {
+ uint64_t addr = 0x1000;
+ RegsArm regs;
+ regs[ARM_REG_PC] = 0x5000;
+ regs[ARM_REG_SP] = addr;
+
+ elf_memory_->SetData32(0x5000, pc_data);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[ARM_REG_SP]);
+ EXPECT_EQ(0x370U, regs[ARM_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x370U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_rt) {
+ // Form 1
+ ArmStepIfSignalHandlerRt(0xe3a070ad);
+
+ // Form 2
+ ArmStepIfSignalHandlerRt(0xef9000ad);
+
+ // Form 3
+ ArmStepIfSignalHandlerRt(0xdf0027ad);
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm64_step_if_signal_handler) {
+ uint64_t addr = 0x1000;
+ RegsArm64 regs;
+ regs[ARM64_REG_PC] = 0x8000;
+ regs[ARM64_REG_SP] = addr;
+
+ elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x460U, regs[ARM64_REG_SP]);
+ EXPECT_EQ(0x470U, regs[ARM64_REG_PC]);
+ EXPECT_EQ(0x460U, regs.sp());
+ EXPECT_EQ(0x470U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_no_siginfo) {
+ uint64_t addr = 0xa00;
+ RegsX86 regs;
+ regs[X86_REG_EIP] = 0x4100;
+ regs[X86_REG_ESP] = addr;
+
+ elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
+ for (uint64_t index = 0; index <= 25; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x70U, regs[X86_REG_EBP]);
+ EXPECT_EQ(0x80U, regs[X86_REG_ESP]);
+ EXPECT_EQ(0x90U, regs[X86_REG_EBX]);
+ EXPECT_EQ(0xa0U, regs[X86_REG_EDX]);
+ EXPECT_EQ(0xb0U, regs[X86_REG_ECX]);
+ EXPECT_EQ(0xc0U, regs[X86_REG_EAX]);
+ EXPECT_EQ(0xf0U, regs[X86_REG_EIP]);
+ EXPECT_EQ(0x80U, regs.sp());
+ EXPECT_EQ(0xf0U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_siginfo) {
+ uint64_t addr = 0xa00;
+ RegsX86 regs;
+ regs[X86_REG_EIP] = 0x4100;
+ regs[X86_REG_ESP] = addr;
+
+ elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
+ addr += 8;
+ // Pointer to ucontext data.
+ process_memory_.SetData32(addr, 0x8100);
+
+ addr = 0x8100;
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData32(addr + index * 4, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+ EXPECT_EQ(0xb0U, regs[X86_REG_EBP]);
+ EXPECT_EQ(0xc0U, regs[X86_REG_ESP]);
+ EXPECT_EQ(0xd0U, regs[X86_REG_EBX]);
+ EXPECT_EQ(0xe0U, regs[X86_REG_EDX]);
+ EXPECT_EQ(0xf0U, regs[X86_REG_ECX]);
+ EXPECT_EQ(0x100U, regs[X86_REG_EAX]);
+ EXPECT_EQ(0x130U, regs[X86_REG_EIP]);
+ EXPECT_EQ(0xc0U, regs.sp());
+ EXPECT_EQ(0x130U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) {
+ uint64_t addr = 0x500;
+ RegsX86_64 regs;
+ regs[X86_64_REG_RIP] = 0x7000;
+ regs[X86_64_REG_RSP] = addr;
+
+ elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
+ elf_memory_->SetData16(0x7008, 0x0f05);
+
+ for (uint64_t index = 0; index <= 30; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x7000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]);
+ EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]);
+ EXPECT_EQ(0x140U, regs.sp());
+ EXPECT_EQ(0x150U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_non_rt) {
+ uint64_t addr = 0x1000;
+ RegsMips regs;
+ regs[MIPS_REG_PC] = 0x8000;
+ regs[MIPS_REG_SP] = addr;
+
+ elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL);
+
+ for (uint64_t index = 0; index <= 50; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x220U, regs[MIPS_REG_SP]);
+ EXPECT_EQ(0x040U, regs[MIPS_REG_PC]);
+ EXPECT_EQ(0x220U, regs.sp());
+ EXPECT_EQ(0x040U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_rt) {
+ uint64_t addr = 0x1000;
+ RegsMips regs;
+ regs[MIPS_REG_PC] = 0x8000;
+ regs[MIPS_REG_SP] = addr;
+
+ elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[MIPS_REG_SP]);
+ EXPECT_EQ(0x170U, regs[MIPS_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x170U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips64_step_if_signal_handler) {
+ uint64_t addr = 0x1000;
+ RegsMips64 regs;
+ regs[MIPS64_REG_PC] = 0x8000;
+ regs[MIPS64_REG_SP] = addr;
+
+ elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[MIPS64_REG_SP]);
+ EXPECT_EQ(0x600U, regs[MIPS64_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x600U, regs.pc());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
new file mode 100644
index 000000000..0a33e2ff4
--- /dev/null
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+class RegsTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ elf_.reset(new ElfFake(memory_));
+ elf_interface_ = new ElfInterfaceFake(elf_->memory());
+ elf_->FakeSetInterface(elf_interface_);
+ }
+
+ ElfInterfaceFake* elf_interface_;
+ MemoryFake* memory_;
+ std::unique_ptr<ElfFake> elf_;
+};
+
+TEST_F(RegsTest, regs32) {
+ RegsImplFake<uint32_t> regs32(50);
+ ASSERT_EQ(50U, regs32.total_regs());
+
+ uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.RawData());
+ for (size_t i = 0; i < 50; i++) {
+ raw[i] = 0xf0000000 + i;
+ }
+ regs32.set_pc(0xf0120340);
+ regs32.set_sp(0xa0ab0cd0);
+
+ for (size_t i = 0; i < 50; i++) {
+ ASSERT_EQ(0xf0000000U + i, regs32[i]) << "Failed comparing register " << i;
+ }
+
+ ASSERT_EQ(0xf0120340U, regs32.pc());
+ ASSERT_EQ(0xa0ab0cd0U, regs32.sp());
+
+ regs32[32] = 10;
+ ASSERT_EQ(10U, regs32[32]);
+}
+
+TEST_F(RegsTest, regs64) {
+ RegsImplFake<uint64_t> regs64(30);
+ ASSERT_EQ(30U, regs64.total_regs());
+
+ uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.RawData());
+ for (size_t i = 0; i < 30; i++) {
+ raw[i] = 0xf123456780000000UL + i;
+ }
+ regs64.set_pc(0xf123456780102030UL);
+ regs64.set_sp(0xa123456780a0b0c0UL);
+
+ for (size_t i = 0; i < 30; i++) {
+ ASSERT_EQ(0xf123456780000000U + i, regs64[i]) << "Failed reading register " << i;
+ }
+
+ ASSERT_EQ(0xf123456780102030UL, regs64.pc());
+ ASSERT_EQ(0xa123456780a0b0c0UL, regs64.sp());
+
+ regs64[8] = 10;
+ ASSERT_EQ(10U, regs64[8]);
+}
+
+TEST_F(RegsTest, rel_pc) {
+ RegsArm64 arm64;
+ EXPECT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
+ EXPECT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
+
+ RegsX86 x86;
+ EXPECT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
+ EXPECT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
+
+ RegsX86_64 x86_64;
+ EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
+ EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
+
+ RegsMips mips;
+ EXPECT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
+ EXPECT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
+
+ RegsMips64 mips64;
+ EXPECT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
+ EXPECT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
+}
+
+TEST_F(RegsTest, rel_pc_arm) {
+ RegsArm arm;
+
+ // Check fence posts.
+ elf_->FakeSetLoadBias(0);
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
+
+ elf_->FakeSetLoadBias(0x100);
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0xff, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x104, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x103, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x102, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
+
+ // Check thumb instructions handling.
+ elf_->FakeSetLoadBias(0);
+ memory_->SetData32(0x2000, 0);
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
+ memory_->SetData32(0x2000, 0xe000f000);
+ EXPECT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
+
+ elf_->FakeSetLoadBias(0x400);
+ memory_->SetData32(0x2100, 0);
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
+ memory_->SetData32(0x2100, 0xf111f111);
+ EXPECT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
+}
+
+TEST_F(RegsTest, elf_invalid) {
+ RegsArm regs_arm;
+ RegsArm64 regs_arm64;
+ RegsX86 regs_x86;
+ RegsX86_64 regs_x86_64;
+ RegsMips regs_mips;
+ RegsMips64 regs_mips64;
+ MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
+ Elf* invalid_elf = new Elf(nullptr);
+ map_info.elf.reset(invalid_elf);
+
+ regs_arm.set_pc(0x1500);
+ EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
+ EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+ EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x511U, invalid_elf));
+
+ regs_arm64.set_pc(0x1600);
+ EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
+ EXPECT_EQ(4U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
+
+ regs_x86.set_pc(0x1700);
+ EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
+ EXPECT_EQ(1U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
+
+ regs_x86_64.set_pc(0x1800);
+ EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
+ EXPECT_EQ(1U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
+
+ regs_mips.set_pc(0x1900);
+ EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
+ EXPECT_EQ(8U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
+
+ regs_mips64.set_pc(0x1a00);
+ EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
+ EXPECT_EQ(8U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
+}
+
+TEST_F(RegsTest, arm_verify_sp_pc) {
+ RegsArm arm;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+ regs[13] = 0x100;
+ regs[15] = 0x200;
+ EXPECT_EQ(0x100U, arm.sp());
+ EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_verify_sp_pc) {
+ RegsArm64 arm64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+ regs[31] = 0xb100000000ULL;
+ regs[32] = 0xc200000000ULL;
+ EXPECT_EQ(0xb100000000U, arm64.sp());
+ EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_verify_sp_pc) {
+ RegsX86 x86;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+ regs[4] = 0x23450000;
+ regs[8] = 0xabcd0000;
+ EXPECT_EQ(0x23450000U, x86.sp());
+ EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+TEST_F(RegsTest, x86_64_verify_sp_pc) {
+ RegsX86_64 x86_64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
+ regs[7] = 0x1200000000ULL;
+ regs[16] = 0x4900000000ULL;
+ EXPECT_EQ(0x1200000000U, x86_64.sp());
+ EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
+
+TEST_F(RegsTest, mips_verify_sp_pc) {
+ RegsMips mips;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(mips.RawData());
+ regs[29] = 0x100;
+ regs[32] = 0x200;
+ EXPECT_EQ(0x100U, mips.sp());
+ EXPECT_EQ(0x200U, mips.pc());
+}
+
+TEST_F(RegsTest, mips64_verify_sp_pc) {
+ RegsMips64 mips64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(mips64.RawData());
+ regs[29] = 0xb100000000ULL;
+ regs[32] = 0xc200000000ULL;
+ EXPECT_EQ(0xb100000000U, mips64.sp());
+ EXPECT_EQ(0xc200000000U, mips64.pc());
+}
+
+TEST_F(RegsTest, machine_type) {
+ RegsArm arm_regs;
+ EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
+
+ RegsArm64 arm64_regs;
+ EXPECT_EQ(ARCH_ARM64, arm64_regs.Arch());
+
+ RegsX86 x86_regs;
+ EXPECT_EQ(ARCH_X86, x86_regs.Arch());
+
+ RegsX86_64 x86_64_regs;
+ EXPECT_EQ(ARCH_X86_64, x86_64_regs.Arch());
+
+ RegsMips mips_regs;
+ EXPECT_EQ(ARCH_MIPS, mips_regs.Arch());
+
+ RegsMips64 mips64_regs;
+ EXPECT_EQ(ARCH_MIPS64, mips64_regs.Arch());
+}
+
+template <typename RegisterType>
+void clone_test(Regs* regs) {
+ RegisterType* register_values = reinterpret_cast<RegisterType*>(regs->RawData());
+ int num_regs = regs->total_regs();
+ for (int i = 0; i < num_regs; ++i) {
+ register_values[i] = i;
+ }
+
+ std::unique_ptr<Regs> clone(regs->Clone());
+ ASSERT_EQ(regs->total_regs(), clone->total_regs());
+ RegisterType* clone_values = reinterpret_cast<RegisterType*>(clone->RawData());
+ for (int i = 0; i < num_regs; ++i) {
+ EXPECT_EQ(register_values[i], clone_values[i]);
+ EXPECT_NE(&register_values[i], &clone_values[i]);
+ }
+}
+
+TEST_F(RegsTest, clone) {
+ std::vector<std::unique_ptr<Regs>> regs;
+ regs.emplace_back(new RegsArm());
+ regs.emplace_back(new RegsArm64());
+ regs.emplace_back(new RegsX86());
+ regs.emplace_back(new RegsX86_64());
+ regs.emplace_back(new RegsMips());
+ regs.emplace_back(new RegsMips64());
+
+ for (auto& r : regs) {
+ if (r->Is32Bit()) {
+ clone_test<uint32_t>(r.get());
+ } else {
+ clone_test<uint64_t>(r.get());
+ }
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 000000000..c58aeff71
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class SymbolsTest : public ::testing::Test {
+ protected:
+ void SetUp() override { memory_.Clear(); }
+
+ void InitSym(TypeParam* sym, uint32_t st_value, uint32_t st_size, uint32_t st_name) {
+ memset(sym, 0, sizeof(*sym));
+ sym->st_info = STT_FUNC;
+ sym->st_value = st_value;
+ sym->st_size = st_size;
+ sym->st_name = st_name;
+ sym->st_shndx = SHN_COMMON;
+ }
+
+ MemoryFake memory_;
+};
+TYPED_TEST_SUITE_P(SymbolsTest);
+
+TYPED_TEST_P(SymbolsTest, function_bounds_check) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0xfU, func_offset);
+
+ // Check one before and one after the function.
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, no_symbol) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // First verify that we can get the name.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now modify the info field so it's no longer a function.
+ sym.st_info = 0;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+
+ // Set the function back, and set the shndx to UNDEF.
+ sym.st_info = STT_FUNC;
+ sym.st_shndx = SHN_UNDEF;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries) {
+ Symbols symbols(0x1000, sizeof(TypeParam) * 3, sizeof(TypeParam), 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+
+ // Reget some of the others to verify getting one function name doesn't
+ // affect any of the next calls.
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(8U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(0xaU, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) {
+ uint64_t entry_size = sizeof(TypeParam) + 5;
+ Symbols symbols(0x1000, entry_size * 3, entry_size, 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
+ Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
+ Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0xfb);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ // Put the name across the end of the tab.
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x20fb, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ // Verify that we can get the function name properly for both entries.
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now use the symbol table that ends at 0x100.
+ ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
+}
+
+// Verify the entire func table is cached.
+TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
+ Symbols symbols(0x1000, 3 * sizeof(TypeParam), sizeof(TypeParam), 0xa000, 0x1000);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ // Make sure that these entries are not in ascending order.
+ this->InitSym(&sym, 0x5000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x2000, 0x300, 0x200);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x1000, 0x100, 0x300);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ // Do call that should cache all of the entries (except the string data).
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
+ this->memory_.Clear();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
+
+ // Clear the memory and only put the symbol data string data in memory.
+ this->memory_.Clear();
+
+ std::string fake_name;
+ fake_name = "first_entry";
+ this->memory_.SetMemory(0xa100, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "second_entry";
+ this->memory_.SetMemory(0xa200, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "third_entry";
+ this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("first_entry", name);
+ ASSERT_EQ(1U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("second_entry", name);
+ ASSERT_EQ(2U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("third_entry", name);
+ ASSERT_EQ(3U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, get_global) {
+ uint64_t start_offset = 0x1000;
+ uint64_t str_offset = 0xa000;
+ Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000);
+
+ TypeParam sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+ sym.st_name = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x100, "global_0");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_FUNC;
+ sym.st_name = 0x200;
+ sym.st_value = 0x10000;
+ sym.st_size = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x200, "function_0");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+ sym.st_name = 0x300;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x300, "global_1");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_FUNC;
+ sym.st_name = 0x400;
+ sym.st_value = 0x12000;
+ sym.st_size = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x400, "function_1");
+
+ uint64_t offset;
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+
+ EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
+ EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
+
+ std::string name;
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, &this->memory_, &name, &offset));
+ EXPECT_EQ("function_0", name);
+ EXPECT_EQ(2U, offset);
+
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, &this->memory_, &name, &offset));
+ EXPECT_EQ("function_1", name);
+ EXPECT_EQ(4U, offset);
+}
+
+REGISTER_TYPED_TEST_SUITE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+ multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
+ symtab_read_cached, get_global);
+
+typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, SymbolsTest, SymbolsTestTypes);
+
+} // 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 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+ unwindstack::LocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+ std::vector<unwindstack::LocalFrameData>* frame_info =
+ reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+ unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+ TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+ TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+ TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/TestUtils.cpp b/libunwindstack/tests/TestUtils.cpp
new file mode 100644
index 000000000..e76f5f8de
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <malloc.h>
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+namespace unwindstack {
+
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data) {
+ static constexpr size_t kNumLeakLoops = 200;
+ static constexpr size_t kMaxAllowedLeakBytes = 32 * 1024;
+
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < kNumLeakLoops; i++) {
+ unwind_func(data);
+
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (first_allocated_bytes == 0) {
+ first_allocated_bytes = allocated_bytes;
+ } else if (last_allocated_bytes > first_allocated_bytes) {
+ // Check that the memory did not increase too much over the first loop.
+ ASSERT_LE(last_allocated_bytes - first_allocated_bytes, kMaxAllowedLeakBytes);
+ }
+ last_allocated_bytes = allocated_bytes;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
new file mode 100644
index 000000000..a4d7b9b33
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace unwindstack {
+
+class TestScopedPidReaper {
+ public:
+ TestScopedPidReaper(pid_t pid) : pid_(pid) {}
+ ~TestScopedPidReaper() {
+ kill(pid_, SIGKILL);
+ waitpid(pid_, nullptr, 0);
+ }
+
+ private:
+ pid_t pid_;
+};
+
+inline bool TestQuiescePid(pid_t pid) {
+ siginfo_t si;
+ bool ready = false;
+ // Wait for up to 5 seconds.
+ for (size_t i = 0; i < 5000; i++) {
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ ready = true;
+ break;
+ }
+ usleep(1000);
+ }
+ return ready;
+}
+
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
new file mode 100644
index 000000000..c2bd8363c
--- /dev/null
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -0,0 +1,1739 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryOffline.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+ parts->Add(memory);
+}
+
+class UnwindOfflineTest : public ::testing::Test {
+ protected:
+ void TearDown() override {
+ if (cwd_ != nullptr) {
+ ASSERT_EQ(0, chdir(cwd_));
+ }
+ free(cwd_);
+ }
+
+ void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
+ dir_ = TestGetFileDirectory() + "offline/" + file_dir;
+
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString((dir_ + "maps.txt"), &data));
+
+ maps_.reset(new BufferMaps(data.c_str()));
+ ASSERT_TRUE(maps_->Parse());
+
+ if (add_stack) {
+ std::string stack_name(dir_ + "stack.data");
+ struct stat st;
+ if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+ std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+ ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+ process_memory_.reset(stack_memory.release());
+ } else {
+ std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+ for (size_t i = 0;; i++) {
+ stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+ if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+ ASSERT_TRUE(i != 0) << "No stack data files found.";
+ break;
+ }
+ AddMemory(stack_name, stack_memory.get());
+ }
+ process_memory_.reset(stack_memory.release());
+ }
+ }
+
+ switch (arch) {
+ case ARCH_ARM: {
+ RegsArm* regs = new RegsArm;
+ regs_.reset(regs);
+ ReadRegs<uint32_t>(regs, arm_regs_);
+ break;
+ }
+ case ARCH_ARM64: {
+ RegsArm64* regs = new RegsArm64;
+ regs_.reset(regs);
+ ReadRegs<uint64_t>(regs, arm64_regs_);
+ break;
+ }
+ case ARCH_X86: {
+ RegsX86* regs = new RegsX86;
+ regs_.reset(regs);
+ ReadRegs<uint32_t>(regs, x86_regs_);
+ break;
+ }
+ case ARCH_X86_64: {
+ RegsX86_64* regs = new RegsX86_64;
+ regs_.reset(regs);
+ ReadRegs<uint64_t>(regs, x86_64_regs_);
+ break;
+ }
+ default:
+ ASSERT_TRUE(false) << "Unknown arch " << std::to_string(arch);
+ }
+ cwd_ = getcwd(nullptr, 0);
+ // Make dir_ an absolute directory.
+ if (dir_.empty() || dir_[0] != '/') {
+ dir_ = std::string(cwd_) + '/' + dir_;
+ }
+ ASSERT_EQ(0, chdir(dir_.c_str()));
+ }
+
+ template <typename AddressType>
+ void ReadRegs(RegsImpl<AddressType>* regs,
+ const std::unordered_map<std::string, uint32_t>& name_to_reg) {
+ FILE* fp = fopen((dir_ + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ while (!feof(fp)) {
+ uint64_t value;
+ char reg_name[100];
+ ASSERT_EQ(2, fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value));
+ std::string name(reg_name);
+ if (!name.empty()) {
+ // Remove the : from the end.
+ name.resize(name.size() - 1);
+ }
+ auto entry = name_to_reg.find(name);
+ ASSERT_TRUE(entry != name_to_reg.end()) << "Unknown register named " << name;
+ (*regs)[entry->second] = value;
+ }
+ fclose(fp);
+ }
+
+ static std::unordered_map<std::string, uint32_t> arm_regs_;
+ static std::unordered_map<std::string, uint32_t> arm64_regs_;
+ static std::unordered_map<std::string, uint32_t> x86_regs_;
+ static std::unordered_map<std::string, uint32_t> x86_64_regs_;
+
+ char* cwd_ = nullptr;
+ std::string dir_;
+ std::unique_ptr<Regs> regs_;
+ std::unique_ptr<Maps> maps_;
+ std::shared_ptr<Memory> process_memory_;
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm_regs_ = {
+ {"r0", ARM_REG_R0}, {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2}, {"r3", ARM_REG_R3},
+ {"r4", ARM_REG_R4}, {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6}, {"r7", ARM_REG_R7},
+ {"r8", ARM_REG_R8}, {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
+ {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR}, {"pc", ARM_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
+ {"x0", ARM64_REG_R0}, {"x1", ARM64_REG_R1}, {"x2", ARM64_REG_R2},
+ {"x3", ARM64_REG_R3}, {"x4", ARM64_REG_R4}, {"x5", ARM64_REG_R5},
+ {"x6", ARM64_REG_R6}, {"x7", ARM64_REG_R7}, {"x8", ARM64_REG_R8},
+ {"x9", ARM64_REG_R9}, {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+ {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
+ {"x15", ARM64_REG_R15}, {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
+ {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
+ {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+ {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
+ {"x27", ARM64_REG_R27}, {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
+ {"sp", ARM64_REG_SP}, {"lr", ARM64_REG_LR}, {"pc", ARM64_REG_PC},
+ {"pst", ARM64_REG_PSTATE},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
+ {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
+ {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
+ {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_64_regs_ = {
+ {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
+ {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8}, {"r9", X86_64_REG_R9},
+ {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
+ {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
+ {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
+ {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
+};
+
+static std::string DumpFrames(Unwinder& unwinder) {
+ std::string str;
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ str += unwinder.FormatFrame(i) + "\n";
+ }
+ return str;
+}
+
+TEST_F(UnwindOfflineTest, pc_straddle_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+
+ std::unique_ptr<Regs> regs_copy(regs_->Clone());
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0001a9f8 libc.so (abort+64)\n"
+ " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6)\n"
+ " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748)\n"
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
+ frame_info);
+ EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xe9c866f8U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf2da0a1bU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xe9c86728U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xf2da1441U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+ // Display build ids now.
+ unwinder.SetRegs(regs_copy.get());
+ unwinder.SetDisplayBuildID(true);
+ unwinder.Unwind();
+
+ frame_info = DumpFrames(unwinder);
+ ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0001a9f8 libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+ " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
+ frame_info);
+}
+
+TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0006dc49 libandroid_runtime.so "
+ "(android::AndroidRuntime::javaThreadShell(void*)+80)\n"
+ " #01 pc 0006dce5 libandroid_runtime.so "
+ "(android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, "
+ "unsigned int, void**))\n",
+ frame_info);
+ EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf1f6dce5U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xd8fe6958U, unwinder.frames()[1].sp);
+}
+
+TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)\n"
+ " #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n"
+ " #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n"
+ " #03 pc 000000000042d8fc libunwindstack_test "
+ "(unwindstack::RemoteThroughSignal(int, unsigned int)+20)\n"
+ " #04 pc 000000000042d8d8 libunwindstack_test "
+ "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)\n"
+ " #05 pc 0000000000455d70 libunwindstack_test (testing::Test::Run()+392)\n",
+ frame_info);
+ EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x64d09d5078U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fe0d84070U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x64d09d508cU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fe0d84080U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x64d09d88fcU, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fe0d84090U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x64d09d88d8U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fe0d840f0U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x64d0a00d70U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_debug_x86) {
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir_ + "descriptor.data", memory);
+ AddMemory(dir_ + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+ }
+ process_memory_.reset(memory);
+
+ JitDebug jit_debug(process_memory_);
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)\n"
+ " #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
+ " #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ "boolean)+136)\n"
+ " #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
+ " #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #05 pc 00146ab5 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
+ " #06 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #07 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #08 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #11 pc 0000fe03 anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
+ " #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #13 pc 00146ab5 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
+ " #14 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #15 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #16 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #19 pc 0000fd3b anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+ "java.lang.Object)+107)\n"
+ " #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #21 pc 00146ab5 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
+ " #22 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #23 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #24 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #27 pc 0000fbdb anonymous:ee74c000 (int "
+ "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+ "java.util.Comparator)+331)\n"
+ " #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #29 pc 00146acb libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+907)\n"
+ " #30 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #31 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #32 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #35 pc 0000f624 anonymous:ee74c000 (boolean Main.foo()+164)\n"
+ " #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #37 pc 00146ab5 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
+ " #38 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #39 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #40 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #43 pc 0000eedb anonymous:ee74c000 (void Main.runPrimary()+59)\n"
+ " #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #45 pc 00146ab5 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
+ " #46 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #47 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #48 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #51 pc 0000ac21 anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
+ " #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #53 pc 00146acb libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+907)\n"
+ " #54 pc 0039cf0d libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
+ " #55 pc 00392552 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
+ " #56 pc 0039399a libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
+ " #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #60 pc 00146acb libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+907)\n"
+ " #61 pc 005aac95 libartd.so "
+ "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+ "art::ArgArray*, art::JValue*, char const*)+85)\n"
+ " #62 pc 005aab5a libartd.so "
+ "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+ "_jmethodID*, char*)+362)\n"
+ " #63 pc 0048a3dd libartd.so "
+ "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)\n"
+ " #64 pc 0018448c libartd.so "
+ "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, "
+ "art::Primitive::Type, art::InvokeType)+1964)\n"
+ " #65 pc 0017cf06 libartd.so "
+ "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)\n"
+ " #66 pc 00001d8c dalvikvm32 "
+ "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)\n"
+ " #67 pc 00001a80 dalvikvm32 (main+1312)\n"
+ " #68 pc 00018275 libc.so\n",
+ frame_info);
+ EXPECT_EQ(0xeb89bfb8U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffeb5280U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xeb89af00U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xee75be80U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffeb5d30U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffeb5d60U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffeb5d80U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffeb5e20U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffeb5ec0U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffeb5f40U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xee75be03U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffeb6160U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffeb6180U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffeb61b0U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffeb6250U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffeb62f0U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffeb6370U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
+ EXPECT_EQ(0xee75bd3bU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xffeb6580U, unwinder.frames()[19].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[20].pc);
+ EXPECT_EQ(0xffeb65b0U, unwinder.frames()[20].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[21].pc);
+ EXPECT_EQ(0xffeb65e0U, unwinder.frames()[21].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[22].pc);
+ EXPECT_EQ(0xffeb6680U, unwinder.frames()[22].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[23].pc);
+ EXPECT_EQ(0xffeb6720U, unwinder.frames()[23].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[24].pc);
+ EXPECT_EQ(0xffeb67a0U, unwinder.frames()[24].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[25].pc);
+ EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
+ EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
+ EXPECT_EQ(0xee75bbdbU, unwinder.frames()[27].pc);
+ EXPECT_EQ(0xffeb69b0U, unwinder.frames()[27].sp);
+ EXPECT_EQ(0xf728e6a2U, unwinder.frames()[28].pc);
+ EXPECT_EQ(0xffeb69f0U, unwinder.frames()[28].sp);
+ EXPECT_EQ(0xf6d27acbU, unwinder.frames()[29].pc);
+ EXPECT_EQ(0xffeb6a20U, unwinder.frames()[29].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[30].pc);
+ EXPECT_EQ(0xffeb6ac0U, unwinder.frames()[30].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[31].pc);
+ EXPECT_EQ(0xffeb6b60U, unwinder.frames()[31].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[32].pc);
+ EXPECT_EQ(0xffeb6be0U, unwinder.frames()[32].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[33].pc);
+ EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
+ EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
+ EXPECT_EQ(0xee75b624U, unwinder.frames()[35].pc);
+ EXPECT_EQ(0xffeb6e20U, unwinder.frames()[35].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[36].pc);
+ EXPECT_EQ(0xffeb6e50U, unwinder.frames()[36].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[37].pc);
+ EXPECT_EQ(0xffeb6e70U, unwinder.frames()[37].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[38].pc);
+ EXPECT_EQ(0xffeb6f10U, unwinder.frames()[38].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[39].pc);
+ EXPECT_EQ(0xffeb6fb0U, unwinder.frames()[39].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[40].pc);
+ EXPECT_EQ(0xffeb7030U, unwinder.frames()[40].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[41].pc);
+ EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
+ EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
+ EXPECT_EQ(0xee75aedbU, unwinder.frames()[43].pc);
+ EXPECT_EQ(0xffeb7240U, unwinder.frames()[43].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[44].pc);
+ EXPECT_EQ(0xffeb72a0U, unwinder.frames()[44].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[45].pc);
+ EXPECT_EQ(0xffeb72c0U, unwinder.frames()[45].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[46].pc);
+ EXPECT_EQ(0xffeb7360U, unwinder.frames()[46].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[47].pc);
+ EXPECT_EQ(0xffeb7400U, unwinder.frames()[47].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[48].pc);
+ EXPECT_EQ(0xffeb7480U, unwinder.frames()[48].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[49].pc);
+ EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
+ EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
+ EXPECT_EQ(0xee756c21U, unwinder.frames()[51].pc);
+ EXPECT_EQ(0xffeb76d0U, unwinder.frames()[51].sp);
+ EXPECT_EQ(0xf728e6a2U, unwinder.frames()[52].pc);
+ EXPECT_EQ(0xffeb76f0U, unwinder.frames()[52].sp);
+ EXPECT_EQ(0xf6d27acbU, unwinder.frames()[53].pc);
+ EXPECT_EQ(0xffeb7710U, unwinder.frames()[53].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[54].pc);
+ EXPECT_EQ(0xffeb77b0U, unwinder.frames()[54].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[55].pc);
+ EXPECT_EQ(0xffeb7850U, unwinder.frames()[55].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[56].pc);
+ EXPECT_EQ(0xffeb78d0U, unwinder.frames()[56].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[57].pc);
+ EXPECT_EQ(0xffeb7940U, unwinder.frames()[57].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[58].pc);
+ EXPECT_EQ(0xffeb7a80U, unwinder.frames()[58].sp);
+ EXPECT_EQ(0xf728e6a2U, unwinder.frames()[59].pc);
+ EXPECT_EQ(0xffeb7ad0U, unwinder.frames()[59].sp);
+ EXPECT_EQ(0xf6d27acbU, unwinder.frames()[60].pc);
+ EXPECT_EQ(0xffeb7af0U, unwinder.frames()[60].sp);
+ EXPECT_EQ(0xf718bc95U, unwinder.frames()[61].pc);
+ EXPECT_EQ(0xffeb7b90U, unwinder.frames()[61].sp);
+ EXPECT_EQ(0xf718bb5aU, unwinder.frames()[62].pc);
+ EXPECT_EQ(0xffeb7c50U, unwinder.frames()[62].sp);
+ EXPECT_EQ(0xf706b3ddU, unwinder.frames()[63].pc);
+ EXPECT_EQ(0xffeb7d10U, unwinder.frames()[63].sp);
+ EXPECT_EQ(0xf6d6548cU, unwinder.frames()[64].pc);
+ EXPECT_EQ(0xffeb7d70U, unwinder.frames()[64].sp);
+ EXPECT_EQ(0xf6d5df06U, unwinder.frames()[65].pc);
+ EXPECT_EQ(0xffeb7df0U, unwinder.frames()[65].sp);
+ EXPECT_EQ(0x56574d8cU, unwinder.frames()[66].pc);
+ EXPECT_EQ(0xffeb7e40U, unwinder.frames()[66].sp);
+ EXPECT_EQ(0x56574a80U, unwinder.frames()[67].pc);
+ EXPECT_EQ(0xffeb7e70U, unwinder.frames()[67].sp);
+ EXPECT_EQ(0xf7363275U, unwinder.frames()[68].pc);
+ EXPECT_EQ(0xffeb7ef0U, unwinder.frames()[68].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_debug_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir_ + "descriptor.data", memory);
+ AddMemory(dir_ + "descriptor1.data", memory);
+ AddMemory(dir_ + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+ }
+ process_memory_.reset(memory);
+
+ JitDebug jit_debug(process_memory_);
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
+ " #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ "boolean)+92)\n"
+ " #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
+ " #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #04 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #05 pc 000bf7a9 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
+ " #06 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #07 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #08 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #09 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #10 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #11 pc 00011c31 anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
+ " #12 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #13 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #14 pc 000bf7a9 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
+ " #15 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #16 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #17 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #18 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #19 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #20 pc 00011b77 anonymous:e2796000 (int Main.compare(java.lang.Object, "
+ "java.lang.Object)+118)\n"
+ " #21 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #22 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #23 pc 000bf7a9 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
+ " #24 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #25 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #26 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #27 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #28 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #29 pc 00011a29 anonymous:e2796000 (int "
+ "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+ "java.util.Comparator)+304)\n"
+ " #30 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #31 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
+ " #32 pc 000bf7bb libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+882)\n"
+ " #33 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #34 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #35 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #36 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #37 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #38 pc 0001139b anonymous:e2796000 (boolean Main.foo()+178)\n"
+ " #39 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #40 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #41 pc 000bf7a9 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
+ " #42 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #43 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #44 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #45 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #46 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #47 pc 00010aa7 anonymous:e2796000 (void Main.runPrimary()+70)\n"
+ " #48 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #49 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #50 pc 000bf7a9 libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
+ " #51 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #52 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #53 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #54 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #55 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #56 pc 0000ba99 anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
+ " #57 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #58 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
+ " #59 pc 000bf7bb libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+882)\n"
+ " #60 pc 00247833 libartd.so "
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
+ " #61 pc 0022e935 libartd.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
+ " #62 pc 0022f71d libartd.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
+ " #63 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #64 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #65 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #66 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
+ " #67 pc 000bf7bb libartd.so "
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+882)\n"
+ " #68 pc 003b292d libartd.so "
+ "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+ "art::ArgArray*, art::JValue*, char const*)+52)\n"
+ " #69 pc 003b26c3 libartd.so "
+ "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+ "_jmethodID*, std::__va_list)+210)\n"
+ " #70 pc 00308411 libartd.so "
+ "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)\n"
+ " #71 pc 000e6a9f libartd.so "
+ "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, "
+ "std::__va_list, art::Primitive::Type, art::InvokeType)+1486)\n"
+ " #72 pc 000e19b9 libartd.so "
+ "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)\n"
+ " #73 pc 0000159f dalvikvm32 "
+ "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)\n"
+ " #74 pc 00001349 dalvikvm32 (main+896)\n"
+ " #75 pc 000850c9 libc.so\n",
+ frame_info);
+ EXPECT_EQ(0xdfe66a5eU, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xff85d180U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xe044712dU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xff85d200U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xe27a7cb1U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xff85d290U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xff85d2b0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xff85d2e8U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xff85d370U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xff85d3d8U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xff85d428U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xff85d470U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xff85d4b0U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xff85d5d0U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xe27a7c31U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xff85d640U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xff85d660U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xff85d698U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xff85d720U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xff85d788U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xff85d7d8U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xff85d820U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xff85d860U, unwinder.frames()[18].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xff85d970U, unwinder.frames()[19].sp);
+ EXPECT_EQ(0xe27a7b77U, unwinder.frames()[20].pc);
+ EXPECT_EQ(0xff85d9e0U, unwinder.frames()[20].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[21].pc);
+ EXPECT_EQ(0xff85da10U, unwinder.frames()[21].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[22].pc);
+ EXPECT_EQ(0xff85da48U, unwinder.frames()[22].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[23].pc);
+ EXPECT_EQ(0xff85dad0U, unwinder.frames()[23].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[24].pc);
+ EXPECT_EQ(0xff85db38U, unwinder.frames()[24].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[25].pc);
+ EXPECT_EQ(0xff85db88U, unwinder.frames()[25].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[26].pc);
+ EXPECT_EQ(0xff85dbd0U, unwinder.frames()[26].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[27].pc);
+ EXPECT_EQ(0xff85dc10U, unwinder.frames()[27].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[28].pc);
+ EXPECT_EQ(0xff85dd20U, unwinder.frames()[28].sp);
+ EXPECT_EQ(0xe27a7a29U, unwinder.frames()[29].pc);
+ EXPECT_EQ(0xff85dd90U, unwinder.frames()[29].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[30].pc);
+ EXPECT_EQ(0xff85ddc0U, unwinder.frames()[30].sp);
+ EXPECT_EQ(0xed76122fU, unwinder.frames()[31].pc);
+ EXPECT_EQ(0xff85de08U, unwinder.frames()[31].sp);
+ EXPECT_EQ(0xed3b97bbU, unwinder.frames()[32].pc);
+ EXPECT_EQ(0xff85de90U, unwinder.frames()[32].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[33].pc);
+ EXPECT_EQ(0xff85def8U, unwinder.frames()[33].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[34].pc);
+ EXPECT_EQ(0xff85df48U, unwinder.frames()[34].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[35].pc);
+ EXPECT_EQ(0xff85df90U, unwinder.frames()[35].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[36].pc);
+ EXPECT_EQ(0xff85dfd0U, unwinder.frames()[36].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[37].pc);
+ EXPECT_EQ(0xff85e110U, unwinder.frames()[37].sp);
+ EXPECT_EQ(0xe27a739bU, unwinder.frames()[38].pc);
+ EXPECT_EQ(0xff85e180U, unwinder.frames()[38].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[39].pc);
+ EXPECT_EQ(0xff85e1b0U, unwinder.frames()[39].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[40].pc);
+ EXPECT_EQ(0xff85e1e0U, unwinder.frames()[40].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[41].pc);
+ EXPECT_EQ(0xff85e268U, unwinder.frames()[41].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[42].pc);
+ EXPECT_EQ(0xff85e2d0U, unwinder.frames()[42].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[43].pc);
+ EXPECT_EQ(0xff85e320U, unwinder.frames()[43].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[44].pc);
+ EXPECT_EQ(0xff85e368U, unwinder.frames()[44].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[45].pc);
+ EXPECT_EQ(0xff85e3a8U, unwinder.frames()[45].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[46].pc);
+ EXPECT_EQ(0xff85e4c0U, unwinder.frames()[46].sp);
+ EXPECT_EQ(0xe27a6aa7U, unwinder.frames()[47].pc);
+ EXPECT_EQ(0xff85e530U, unwinder.frames()[47].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[48].pc);
+ EXPECT_EQ(0xff85e5a0U, unwinder.frames()[48].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[49].pc);
+ EXPECT_EQ(0xff85e5d8U, unwinder.frames()[49].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[50].pc);
+ EXPECT_EQ(0xff85e660U, unwinder.frames()[50].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[51].pc);
+ EXPECT_EQ(0xff85e6c8U, unwinder.frames()[51].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[52].pc);
+ EXPECT_EQ(0xff85e718U, unwinder.frames()[52].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[53].pc);
+ EXPECT_EQ(0xff85e760U, unwinder.frames()[53].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[54].pc);
+ EXPECT_EQ(0xff85e7a0U, unwinder.frames()[54].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[55].pc);
+ EXPECT_EQ(0xff85e8f0U, unwinder.frames()[55].sp);
+ EXPECT_EQ(0xe27a1a99U, unwinder.frames()[56].pc);
+ EXPECT_EQ(0xff85e960U, unwinder.frames()[56].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[57].pc);
+ EXPECT_EQ(0xff85e990U, unwinder.frames()[57].sp);
+ EXPECT_EQ(0xed76122fU, unwinder.frames()[58].pc);
+ EXPECT_EQ(0xff85e9c8U, unwinder.frames()[58].sp);
+ EXPECT_EQ(0xed3b97bbU, unwinder.frames()[59].pc);
+ EXPECT_EQ(0xff85ea50U, unwinder.frames()[59].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[60].pc);
+ EXPECT_EQ(0xff85eab8U, unwinder.frames()[60].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[61].pc);
+ EXPECT_EQ(0xff85eb08U, unwinder.frames()[61].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[62].pc);
+ EXPECT_EQ(0xff85eb50U, unwinder.frames()[62].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[63].pc);
+ EXPECT_EQ(0xff85eb90U, unwinder.frames()[63].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[64].pc);
+ EXPECT_EQ(0xff85ec90U, unwinder.frames()[64].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[65].pc);
+ EXPECT_EQ(0xff85ed00U, unwinder.frames()[65].sp);
+ EXPECT_EQ(0xed76122fU, unwinder.frames()[66].pc);
+ EXPECT_EQ(0xff85ed38U, unwinder.frames()[66].sp);
+ EXPECT_EQ(0xed3b97bbU, unwinder.frames()[67].pc);
+ EXPECT_EQ(0xff85edc0U, unwinder.frames()[67].sp);
+ EXPECT_EQ(0xed6ac92dU, unwinder.frames()[68].pc);
+ EXPECT_EQ(0xff85ee28U, unwinder.frames()[68].sp);
+ EXPECT_EQ(0xed6ac6c3U, unwinder.frames()[69].pc);
+ EXPECT_EQ(0xff85eeb8U, unwinder.frames()[69].sp);
+ EXPECT_EQ(0xed602411U, unwinder.frames()[70].pc);
+ EXPECT_EQ(0xff85ef48U, unwinder.frames()[70].sp);
+ EXPECT_EQ(0xed3e0a9fU, unwinder.frames()[71].pc);
+ EXPECT_EQ(0xff85ef90U, unwinder.frames()[71].sp);
+ EXPECT_EQ(0xed3db9b9U, unwinder.frames()[72].pc);
+ EXPECT_EQ(0xff85f008U, unwinder.frames()[72].sp);
+ EXPECT_EQ(0xab0d459fU, unwinder.frames()[73].pc);
+ EXPECT_EQ(0xff85f038U, unwinder.frames()[73].sp);
+ EXPECT_EQ(0xab0d4349U, unwinder.frames()[74].pc);
+ EXPECT_EQ(0xff85f050U, unwinder.frames()[74].sp);
+ EXPECT_EQ(0xedb0d0c9U, unwinder.frames()[75].pc);
+ EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
+}
+
+struct LeakType {
+ LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory)
+ : maps(maps), regs(regs), process_memory(process_memory) {}
+
+ Maps* maps;
+ Regs* regs;
+ std::shared_ptr<Memory>& process_memory;
+};
+
+static void OfflineUnwind(void* data) {
+ LeakType* leak_data = reinterpret_cast<LeakType*>(data);
+
+ std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
+ JitDebug jit_debug(leak_data->process_memory);
+ Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
+ unwinder.Unwind();
+ ASSERT_EQ(76U, unwinder.NumFrames());
+}
+
+TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir_ + "descriptor.data", memory);
+ AddMemory(dir_ + "descriptor1.data", memory);
+ AddMemory(dir_ + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+ }
+ process_memory_.reset(memory);
+
+ LeakType data(maps_.get(), regs_.get(), process_memory_);
+ TestCheckForLeaks(OfflineUnwind, &data);
+}
+
+// The eh_frame_hdr data is present but set to zero fdes. This should
+// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
+// No .gnu_debugdata section in the elf file, so no symbols.
+TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0000000000000550 waiter64\n"
+ " #01 pc 0000000000000568 waiter64\n"
+ " #02 pc 000000000000057c waiter64\n"
+ " #03 pc 0000000000000590 waiter64\n"
+ " #04 pc 00000000000a8e98 libc.so (__libc_init+88)\n",
+ frame_info);
+ EXPECT_EQ(0x60a9fdf550U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fdd141990U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x60a9fdf568U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fdd1419a0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x60a9fdf57cU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fdd1419b0U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x60a9fdf590U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fdd1419c0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7542d68e98U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fdd1419d0U, unwinder.frames()[4].sp);
+}
+
+// The elf has bad eh_frame unwind information for the pcs. If eh_frame
+// is used first, the unwind will not match the expected output.
+TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000685 waiter (call_level3+53)\n"
+ " #01 pc 000006b7 waiter (call_level2+23)\n"
+ " #02 pc 000006d7 waiter (call_level1+23)\n"
+ " #03 pc 000006f7 waiter (main+23)\n"
+ " #04 pc 00018275 libc.so\n",
+ frame_info);
+ EXPECT_EQ(0x56598685U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffcf9e38U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x565986b7U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffcf9e50U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x565986d7U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffcf9e60U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x565986f7U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffcf9e70U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf744a275U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffcf9e80U, unwinder.frames()[4].sp);
+}
+
+// Make sure that a pc that is at the beginning of an fde unwinds correctly.
+TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
+ ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0000000000000a80 unwind_test64 (calling3)\n"
+ " #01 pc 0000000000000dd9 unwind_test64 (calling2+633)\n"
+ " #02 pc 000000000000121e unwind_test64 (calling1+638)\n"
+ " #03 pc 00000000000013ed unwind_test64 (main+13)\n"
+ " #04 pc 00000000000202b0 libc.so\n",
+ frame_info);
+ EXPECT_EQ(0x561550b17a80U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffcc8596ce8U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x561550b17dd9U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffcc8596cf0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x561550b1821eU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffcc8596f40U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x561550b183edU, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffcc8597190U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7f4de62162b0U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffcc85971a0U, unwinder.frames()[4].sp);
+}
+
+TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir_ + "descriptor.data", memory);
+ AddMemory(dir_ + "stack.data", memory);
+ for (size_t i = 0; i < 2; i++) {
+ AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+ }
+ process_memory_.reset(memory);
+
+ JitDebug jit_debug(process_memory_);
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(25U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0000c788 <anonymous:d0250000> "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
+ " #01 pc 0000cdd5 <anonymous:d0250000> "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
+ " #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
+ " #03 pc 002657a5 libart.so "
+ "(art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, "
+ "art::JValue*)+876)\n"
+ " #04 pc 004021a7 libart.so (MterpMaybeDoOnStackReplacement+86)\n"
+ " #05 pc 00412474 libart.so (ExecuteMterpImpl+66164)\n"
+ " #06 pc cd8365b0 <unknown>\n" // symbol in dex file
+ " #07 pc 001d7f1b libart.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+374)\n"
+ " #08 pc 001dc593 libart.so "
+ "(art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, "
+ "art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)\n"
+ " #09 pc 001f4d01 libart.so "
+ "(bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, "
+ "art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)\n"
+ " #10 pc 003fe427 libart.so (MterpInvokeInterface+1354)\n"
+ " #11 pc 00405b94 libart.so (ExecuteMterpImpl+14740)\n"
+ " #12 pc 7004873e <unknown>\n" // symbol in dex file
+ " #13 pc 001d7f1b libart.so "
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+374)\n"
+ " #14 pc 001dc4d5 libart.so "
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+92)\n"
+ " #15 pc 003f25ab libart.so (artQuickToInterpreterBridge+970)\n"
+ " #16 pc 00417aff libart.so (art_quick_to_interpreter_bridge+30)\n"
+ " #17 pc 00413575 libart.so (art_quick_invoke_stub_internal+68)\n"
+ " #18 pc 00418531 libart.so (art_quick_invoke_stub+236)\n"
+ " #19 pc 000b468d libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned "
+ "int, art::JValue*, char const*)+136)\n"
+ " #20 pc 00362f49 libart.so "
+ "(art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable "
+ "const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char "
+ "const*)+52)\n"
+ " #21 pc 00363cd9 libart.so "
+ "(art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, "
+ "_jobject*, _jmethodID*, jvalue*)+332)\n"
+ " #22 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)\n"
+ " #23 pc 00062925 libc.so (__pthread_start(void*)+22)\n"
+ " #24 pc 0001de39 libc.so (__start_thread+24)\n",
+ frame_info);
+ EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xe4a755bbU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xcd4ff160U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xe48c77a5U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xcd4ff190U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xe4a641a7U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xcd4ff298U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xe4a74474U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xcd4ff2b8U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xcd8365b0U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xcd4ff2e0U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xe4839f1bU, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xcd4ff2e0U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xe483e593U, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xcd4ff330U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xe4856d01U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xcd4ff380U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xe4a60427U, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xcd4ff430U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xe4a67b94U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xcd4ff498U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x7004873eU, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xcd4ff4c0U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xe4839f1bU, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xcd4ff4c0U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xe483e4d5U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xcd4ff510U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xe4a545abU, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xcd4ff538U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xe4a79affU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xcd4ff640U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xe4a75575U, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xcd4ff6b0U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0xe4a7a531U, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xcd4ff6e8U, unwinder.frames()[18].sp);
+ EXPECT_EQ(0xe471668dU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xcd4ff770U, unwinder.frames()[19].sp);
+ EXPECT_EQ(0xe49c4f49U, unwinder.frames()[20].pc);
+ EXPECT_EQ(0xcd4ff7c8U, unwinder.frames()[20].sp);
+ EXPECT_EQ(0xe49c5cd9U, unwinder.frames()[21].pc);
+ EXPECT_EQ(0xcd4ff850U, unwinder.frames()[21].sp);
+ EXPECT_EQ(0xe49e71ddU, unwinder.frames()[22].pc);
+ EXPECT_EQ(0xcd4ff8e8U, unwinder.frames()[22].sp);
+ EXPECT_EQ(0xe7df3925U, unwinder.frames()[23].pc);
+ EXPECT_EQ(0xcd4ff958U, unwinder.frames()[23].sp);
+ EXPECT_EQ(0xe7daee39U, unwinder.frames()[24].pc);
+ EXPECT_EQ(0xcd4ff960U, unwinder.frames()[24].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_map_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
+
+ maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+ "jit_map0.so", 0);
+ maps_->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+ "jit_map1.so", 0);
+ maps_->Sort();
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000 jit_map0.so "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
+ " #01 pc 0000003d jit_map1.so "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
+ " #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
+
+ " #03 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)\n"
+ " #04 pc 00062925 libc.so (__pthread_start(void*)+22)\n"
+ " #05 pc 0001de39 libc.so (__start_thread+24)\n",
+ frame_info);
+
+ EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xe4a755bbU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xcd4ff160U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xe49e71ddU, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xcd4ff8e8U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xe7df3925U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xcd4ff958U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xe7daee39U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0032bfa0 libunwindstack_test (SignalInnerFunction+40)\n"
+ " #01 pc 0032bfeb libunwindstack_test (SignalMiddleFunction+2)\n"
+ " #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)\n"
+ " #03 pc 0032fed3 libunwindstack_test "
+ "(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
+ " #04 pc 0002652c libc.so (__restore)\n"
+ " #05 pc 00000000 <unknown>\n"
+ " #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)\n"
+ " #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)\n"
+ " #08 pc 0032cc81 libunwindstack_test (OuterFunction+42)\n"
+ " #09 pc 0032e547 libunwindstack_test "
+ "(unwindstack::RemoteThroughSignal(int, unsigned int)+270)\n"
+ " #10 pc 0032ed99 libunwindstack_test "
+ "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)\n"
+ " #11 pc 00354453 libunwindstack_test (testing::Test::Run()+154)\n"
+ " #12 pc 00354de7 libunwindstack_test (testing::TestInfo::Run()+194)\n"
+ " #13 pc 00355105 libunwindstack_test (testing::TestCase::Run()+180)\n"
+ " #14 pc 0035a215 libunwindstack_test "
+ "(testing::internal::UnitTestImpl::RunAllTests()+664)\n"
+ " #15 pc 00359f4f libunwindstack_test (testing::UnitTest::Run()+110)\n"
+ " #16 pc 0034d3db libunwindstack_test (main+38)\n"
+ " #17 pc 00092c0d libc.so (__libc_init+48)\n"
+ " #18 pc 0004202f libunwindstack_test (_start_main+38)\n",
+ frame_info);
+
+ EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf413652cU, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
+}
+
+// Test using a non-zero load bias library that has the fde entries
+// encoded as 0xb, which is not set as pc relative.
+TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0005138c libc.so (__ioctl+8)\n"
+ " #01 pc 0002140f libc.so (ioctl+30)\n"
+ " #02 pc 00039535 libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)\n"
+ " #03 pc 00039633 libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)\n"
+ " #04 pc 00039b57 libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)\n"
+ " #05 pc 00000c21 mediaserver (main+104)\n"
+ " #06 pc 00084b89 libc.so (__libc_init+48)\n"
+ " #07 pc 00000b77 mediaserver (_start_main+38)\n",
+ frame_info);
+
+ EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffd4a638U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xf1a75535U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffd4a650U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xf1a75633U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffd4a6b0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf1a75b57U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffd4a6d0U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x8d1cc21U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffd4a6e8U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xf0c15b89U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffd4a700U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x8d1cb77U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
+ " #01 pc 000000000005426c linker64 "
+ "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+ " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
+ " #03 pc 00000000000846f4 libc.so (abort+172)\n"
+ " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
+ "(ANGLEGetUtilityAPI+56)\n"
+ " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+ // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+ // Add the memory that represents the shared library.
+ MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
+ AddMemory(dir_ + "lib_mem.data", memory);
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
+ " #01 pc 000000000005426c linker64 "
+ "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+ " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
+ " #03 pc 00000000000846f4 libc.so (abort+172)\n"
+ " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)\n"
+ " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+ // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000814bc libc.so (syscall+28)\n"
+ " #01 pc 00000000008cdf5c test.apk (offset 0x5000)\n"
+ " #02 pc 00000000008cde9c test.apk (offset 0x5000)\n"
+ " #03 pc 00000000008cdd70 test.apk (offset 0x5000)\n"
+ " #04 pc 00000000008ce408 test.apk (offset 0x5000)\n"
+ " #05 pc 00000000008ce8d8 test.apk (offset 0x5000)\n"
+ " #06 pc 00000000008ce814 test.apk (offset 0x5000)\n"
+ " #07 pc 00000000008bcf60 test.apk (offset 0x5000)\n"
+ " #08 pc 0000000000133024 test.apk (offset 0x5000)\n"
+ " #09 pc 0000000000134ad0 test.apk (offset 0x5000)\n"
+ " #10 pc 0000000000134b64 test.apk (offset 0x5000)\n"
+ " #11 pc 00000000000e406c libc.so (__pthread_start(void*)+36)\n"
+ " #12 pc 0000000000085e18 libc.so (__start_thread+64)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7be6715e9cULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7be4f07800ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7be6715d70ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7be4f07840ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7be6716408ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7be4f07860ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7be67168d8ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7be4f07880ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7be6716814ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7be4f078f0ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x7be6704f60ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7be4f07910ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x7be5f7b024ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7be4f07950ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x7be5f7cad0ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7be4f07aa0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x7be5f7cb64ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7be4f07ce0ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x7cbe11406cULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7be4f07d00ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x7cbe0b5e18ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
+}
+
+TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("invalid_elf_offset_arm/", ARCH_ARM, false));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(1U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(" #00 pc 00aa7508 invalid.apk (offset 0x12e4000)\n", frame_info);
+ EXPECT_EQ(0xc898f508, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
+}
+
+TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
+ ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000e9dd4 libc.so (__write+20)\n"
+ " #01 pc 000000000007ab9c libc.so (_IO_file_write+44)\n"
+ " #02 pc 0000000000079f3e libc.so\n"
+ " #03 pc 000000000007bce8 libc.so (_IO_do_write+24)\n"
+ " #04 pc 000000000007b26e libc.so (_IO_file_xsputn+270)\n"
+ " #05 pc 000000000004f7f9 libc.so (_IO_vfprintf+1945)\n"
+ " #06 pc 0000000000057cb5 libc.so (_IO_printf+165)\n"
+ " #07 pc 0000000000ed1796 perfetto_unittests "
+ "(testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest "
+ "const&, int)+374)\n"
+ " #08 pc 0000000000ed30fd perfetto_unittests "
+ "(testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, "
+ "int)+125)\n"
+ " #09 pc 0000000000ed5e25 perfetto_unittests "
+ "(testing::internal::UnitTestImpl::RunAllTests()+581)\n"
+ " #10 pc 0000000000ef63f3 perfetto_unittests "
+ "(bool "
+ "testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+ "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+ "const*)+131)\n"
+ " #11 pc 0000000000ee2a21 perfetto_unittests "
+ "(bool "
+ "testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+ "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+ "const*)+113)\n"
+ " #12 pc 0000000000ed5bb9 perfetto_unittests (testing::UnitTest::Run()+185)\n"
+ " #13 pc 0000000000e900f0 perfetto_unittests (RUN_ALL_TESTS()+16)\n"
+ " #14 pc 0000000000e900d8 perfetto_unittests (main+56)\n"
+ " #15 pc 000000000002352a libc.so (__libc_start_main+234)\n"
+ " #16 pc 0000000000919029 perfetto_unittests (_start+41)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7f9326a57dd4ULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffd224153c8ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7f93269e8b9cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffd224153d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7f93269e7f3eULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffd22415400ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7f93269e9ce8ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffd22415440ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7f93269e926eULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffd22415450ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7f93269bd7f9ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7ffd22415490ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7f93269c5cb5ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7ffd22415a10ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xed1796ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7ffd22415af0ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xed30fdULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7ffd22415b70ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xed5e25ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7ffd22415bb0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xef63f3ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7ffd22415c60ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xee2a21ULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7ffd22415cc0ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xed5bb9ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7ffd22415d40ULL, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xe900f0ULL, unwinder.frames()[13].pc);
+ EXPECT_EQ(0x7ffd22415d90ULL, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xe900d8ULL, unwinder.frames()[14].pc);
+ EXPECT_EQ(0x7ffd22415da0ULL, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x7f932699152aULL, unwinder.frames()[15].pc);
+ EXPECT_EQ(0x7ffd22415dd0ULL, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x919029ULL, unwinder.frames()[16].pc);
+ EXPECT_EQ(0x7ffd22415e90ULL, unwinder.frames()[16].sp);
+}
+
+TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("load_bias_different_section_bias_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(12U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000d59bc linker64 (__dl_syscall+28)\n"
+ " #01 pc 00000000000554e8 linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)\n"
+ " #02 pc 00000000000008c0 vdso (__kernel_rt_sigreturn)\n"
+ " #03 pc 000000000007f3e8 libc.so (abort+168)\n"
+ " #04 pc 00000000000459fc test (std::__ndk1::__throw_bad_cast()+4)\n"
+ " #05 pc 0000000000056d80 test (testing::Test::Run()+88)\n"
+ " #06 pc 000000000005724c test (testing::TestInfo::Run()+112)\n"
+ " #07 pc 0000000000057558 test (testing::TestSuite::Run()+116)\n"
+ " #08 pc 000000000005bffc test (testing::internal::UnitTestImpl::RunAllTests()+464)\n"
+ " #09 pc 000000000005bd9c test (testing::UnitTest::Run()+116)\n"
+ " #10 pc 00000000000464e4 test (main+144)\n"
+ " #11 pc 000000000007aa34 libc.so (__libc_init+108)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7112cb99bcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7112c394e8ULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7112be28c0ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7112bdbda0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x71115ab3e8ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fdd4a3f00ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x5f739dc9fcULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fdd4a3fe0ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x5f739edd80ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fdd4a3ff0ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x5f739ee24cULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7fdd4a4010ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x5f739ee558ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7fdd4a4040ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x5f739f2ffcULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7fdd4a4070ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x5f739f2d9cULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7fdd4a4100ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x5f739dd4e4ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7fdd4a4130ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x71115a6a34ULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7fdd4a4170ULL, unwinder.frames()[11].sp);
+}
+
+TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
+ ASSERT_NO_FATAL_FAILURE(Init("eh_frame_bias_x86/", ARCH_X86));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(11U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc ffffe430 vdso.so (__kernel_vsyscall+16)\n"
+ " #01 pc 00082a4b libc.so (__epoll_pwait+43)\n"
+ " #02 pc 000303a3 libc.so (epoll_pwait+115)\n"
+ " #03 pc 000303ed libc.so (epoll_wait+45)\n"
+ " #04 pc 00010ea2 tombstoned (epoll_dispatch+226)\n"
+ " #05 pc 0000c5e7 tombstoned (event_base_loop+1095)\n"
+ " #06 pc 0000c193 tombstoned (event_base_dispatch+35)\n"
+ " #07 pc 00005c77 tombstoned (main+884)\n"
+ " #08 pc 00015f66 libc.so (__libc_init+102)\n"
+ " #09 pc 0000360e tombstoned (_start+98)\n"
+ " #10 pc 00000001 <unknown>\n",
+ frame_info);
+
+ EXPECT_EQ(0xffffe430ULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xfffe1a30ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xeb585a4bULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xfffe1a40ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xeb5333a3ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xfffe1a60ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xeb5333edULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xfffe1ab0ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xeb841ea2ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xfffe1ae0ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xeb83d5e7ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xfffe1b30ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xeb83d193ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xfffe1bd0ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xeb836c77ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xfffe1c00ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xeb518f66ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xfffe1d00ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xeb83460eULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xfffe1d40ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x00000001ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xfffe1d74ULL, unwinder.frames()[10].sp);
+}
+
+TEST_F(UnwindOfflineTest, signal_load_bias_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("signal_load_bias_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0029ef9e libunwindstack_unit_test (SignalInnerFunction+10)\n"
+ " #01 pc 0029efa7 libunwindstack_unit_test (SignalMiddleFunction+2)\n"
+ " #02 pc 0029efaf libunwindstack_unit_test (SignalOuterFunction+2)\n"
+ " #03 pc 002a280b libunwindstack_unit_test (unwindstack::SignalCallerHandler(int, "
+ "siginfo*, void*)+10)\n"
+ " #04 pc 00058bd4 libc.so (__restore)\n"
+ " #05 pc 0029f01e libunwindstack_unit_test (InnerFunction+106)\n"
+ " #06 pc 0029f633 libunwindstack_unit_test (MiddleFunction+16)\n"
+ " #07 pc 0029f64b libunwindstack_unit_test (OuterFunction+16)\n"
+ " #08 pc 002a1711 libunwindstack_unit_test (unwindstack::RemoteThroughSignal(int, unsigned "
+ "int)+260)\n"
+ " #09 pc 002a1603 libunwindstack_unit_test "
+ "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+10)\n"
+ " #10 pc 002c8fe3 libunwindstack_unit_test (testing::Test::Run()+130)\n"
+ " #11 pc 002c9b25 libunwindstack_unit_test (testing::TestInfo::Run()+184)\n"
+ " #12 pc 002c9e27 libunwindstack_unit_test (testing::TestSuite::Run()+202)\n"
+ " #13 pc 002d193d libunwindstack_unit_test "
+ "(testing::internal::UnitTestImpl::RunAllTests()+660)\n"
+ " #14 pc 002d160b libunwindstack_unit_test (testing::UnitTest::Run()+134)\n"
+ " #15 pc 002de035 libunwindstack_unit_test (IsolateMain+680)\n"
+ " #16 pc 00058155 libc.so (__libc_init+68)\n",
+ frame_info);
+
+ EXPECT_EQ(0xb6955f9eULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xf2790ce8ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xb6955fa7ULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xf2790ce8ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xb6955fafULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xf2790cf0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xb695980bULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xf2790cf8ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf23febd4ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xf2790d10ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xb695601eULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffe67798ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xb6956633ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffe67890ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xb695664bULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffe678a0ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xb6958711ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffe678b0ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xb6958603ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffe67ac8ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xb697ffe3ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffe67ad8ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xb6980b25ULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffe67ae8ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xb6980e27ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffe67b18ULL, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xb698893dULL, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffe67b48ULL, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xb698860bULL, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffe67bb0ULL, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xb6995035ULL, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffe67bd0ULL, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xf23fe155ULL, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffe67d10ULL, unwinder.frames()[16].sp);
+}
+
+TEST_F(UnwindOfflineTest, empty_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("empty_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000963a4 libc.so (__ioctl+4)\n"
+ " #01 pc 000000000005344c libc.so (ioctl+140)\n"
+ " #02 pc 0000000000050ce4 libbinder.so "
+ "(android::IPCThreadState::talkWithDriver(bool)+308)\n"
+ " #03 pc 0000000000050e98 libbinder.so "
+ "(android::IPCThreadState::getAndExecuteCommand()+24)\n"
+ " #04 pc 00000000000516ac libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)\n"
+ " #05 pc 00000000000443b0 netd (main+1056)\n"
+ " #06 pc 0000000000045594 libc.so (__libc_init+108)\n",
+ frame_info);
+
+ EXPECT_EQ(0x72a02203a4U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x72a01dd44cU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x729f759ce4U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffb6c0c50U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x729f759e98U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffb6c0ce0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x729f75a6acU, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffb6c0d10U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x5d478af3b0U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7ffb6c0d40U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x72a01cf594U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7ffb6c0f30U, unwinder.frames()[6].sp);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 000000000..f76a101df
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+#include "MemoryRemote.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+enum TestTypeEnum : uint8_t {
+ TEST_TYPE_LOCAL_UNWINDER = 0,
+ TEST_TYPE_LOCAL_UNWINDER_FROM_PID,
+ TEST_TYPE_LOCAL_WAIT_FOR_FINISH,
+ TEST_TYPE_REMOTE,
+ TEST_TYPE_REMOTE_WITH_INVALID_CALL,
+};
+
+static std::atomic_bool g_ready;
+static volatile bool g_ready_for_remote;
+static volatile bool g_signal_ready_for_remote;
+static std::atomic_bool g_finish;
+static std::atomic_uintptr_t g_ucontext;
+
+static void ResetGlobals() {
+ g_ready = false;
+ g_ready_for_remote = false;
+ g_signal_ready_for_remote = false;
+ g_finish = false;
+ g_ucontext = 0;
+}
+
+static std::vector<const char*> kFunctionOrder{"OuterFunction", "MiddleFunction", "InnerFunction"};
+
+static std::vector<const char*> kFunctionSignalOrder{"OuterFunction", "MiddleFunction",
+ "InnerFunction", "SignalOuterFunction",
+ "SignalMiddleFunction", "SignalInnerFunction"};
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+ g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+ while (!g_finish.load()) {
+ }
+}
+
+extern "C" void SignalInnerFunction() {
+ g_signal_ready_for_remote = true;
+ // Avoid any function calls because not every instruction will be
+ // unwindable.
+ // This method of looping is only used when testing a remote unwind.
+ while (true) {
+ }
+}
+
+extern "C" void SignalMiddleFunction() {
+ SignalInnerFunction();
+}
+
+extern "C" void SignalOuterFunction() {
+ SignalMiddleFunction();
+}
+
+static void SignalCallerHandler(int, siginfo_t*, void*) {
+ SignalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder* unwinder) {
+ std::string unwind;
+ for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+ unwind += unwinder->FormatFrame(i) + '\n';
+ }
+
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+static void VerifyUnwind(Unwinder* unwinder, std::vector<const char*> expected_function_names) {
+ unwinder->Unwind();
+
+ for (auto& frame : unwinder->frames()) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
+}
+
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+ std::vector<const char*> expected_function_names) {
+ auto process_memory(Memory::CreateProcessMemory(pid));
+
+ Unwinder unwinder(512, maps, regs, process_memory);
+ VerifyUnwind(&unwinder, expected_function_names);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(TestTypeEnum test_type) {
+ if (test_type == TEST_TYPE_LOCAL_WAIT_FOR_FINISH) {
+ while (!g_finish.load()) {
+ }
+ return;
+ }
+ if (test_type == TEST_TYPE_REMOTE || test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
+ g_ready_for_remote = true;
+ g_ready = true;
+ if (test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
+ void (*crash_func)() = nullptr;
+ crash_func();
+ }
+ // Avoid any function calls because not every instruction will be
+ // unwindable.
+ // This method of looping is only used when testing a remote unwind.
+ while (true) {
+ }
+ return;
+ }
+
+ std::unique_ptr<Unwinder> unwinder;
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+ std::unique_ptr<Maps> maps;
+
+ if (test_type == TEST_TYPE_LOCAL_UNWINDER) {
+ maps.reset(new LocalMaps());
+ ASSERT_TRUE(maps->Parse());
+ auto process_memory(Memory::CreateProcessMemory(getpid()));
+ unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
+ } else {
+ UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
+ ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
+ unwinder_from_pid->SetRegs(regs.get());
+ unwinder.reset(unwinder_from_pid);
+ }
+ VerifyUnwind(unwinder.get(), kFunctionOrder);
+}
+
+extern "C" void MiddleFunction(TestTypeEnum test_type) {
+ InnerFunction(test_type);
+}
+
+extern "C" void OuterFunction(TestTypeEnum test_type) {
+ MiddleFunction(test_type);
+}
+
+class UnwindTest : public ::testing::Test {
+ public:
+ void SetUp() override { ResetGlobals(); }
+};
+
+TEST_F(UnwindTest, local) {
+ OuterFunction(TEST_TYPE_LOCAL_UNWINDER);
+}
+
+TEST_F(UnwindTest, local_use_from_pid) {
+ OuterFunction(TEST_TYPE_LOCAL_UNWINDER_FROM_PID);
+}
+
+static void LocalUnwind(void* data) {
+ TestTypeEnum* test_type = reinterpret_cast<TestTypeEnum*>(data);
+ OuterFunction(*test_type);
+}
+
+TEST_F(UnwindTest, local_check_for_leak) {
+ TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER;
+ TestCheckForLeaks(LocalUnwind, &test_type);
+}
+
+TEST_F(UnwindTest, local_use_from_pid_check_for_leak) {
+ TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER_FROM_PID;
+ TestCheckForLeaks(LocalUnwind, &test_type);
+}
+
+void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
+ *completed = false;
+ // Need to sleep before attempting first ptrace. Without this, on the
+ // host it becomes impossible to attach and ptrace sets errno to EPERM.
+ usleep(1000);
+ for (size_t i = 0; i < 1000; i++) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ ASSERT_TRUE(TestQuiescePid(pid))
+ << "Waiting for process to quiesce failed: " << strerror(errno);
+
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
+ *completed = true;
+ }
+ if (!*completed || !leave_attached) {
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ }
+ if (*completed) {
+ break;
+ }
+ } else {
+ ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
+ }
+ usleep(5000);
+ }
+}
+
+TEST_F(UnwindTest, remote) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(TEST_TYPE_REMOTE);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, unwind_from_pid_remote) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(TEST_TYPE_REMOTE);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ UnwinderFromPid unwinder(512, pid);
+ ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ unwinder.SetRegs(regs.get());
+
+ VerifyUnwind(&unwinder, kFunctionOrder);
+
+ // Verify that calling the same object works again.
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(TEST_TYPE_REMOTE);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+ TestCheckForLeaks(unwind_func, &pid);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+static void RemoteUnwind(void* data) {
+ pid_t* pid = reinterpret_cast<pid_t*>(data);
+
+ RemoteMaps maps(*pid);
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(*pid, &maps, regs.get(), kFunctionOrder);
+}
+
+TEST_F(UnwindTest, remote_check_for_leaks) {
+ RemoteCheckForLeaks(RemoteUnwind);
+}
+
+static void RemoteUnwindFromPid(void* data) {
+ pid_t* pid = reinterpret_cast<pid_t*>(data);
+
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ UnwinderFromPid unwinder(512, *pid);
+ ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ unwinder.SetRegs(regs.get());
+
+ VerifyUnwind(&unwinder, kFunctionOrder);
+}
+
+TEST_F(UnwindTest, remote_unwind_for_pid_check_for_leaks) {
+ RemoteCheckForLeaks(RemoteUnwindFromPid);
+}
+
+TEST_F(UnwindTest, from_context) {
+ std::atomic_int tid(0);
+ std::thread thread([&]() {
+ tid = syscall(__NR_gettid);
+ OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
+ });
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+ // Wait for the tid to get set.
+ for (size_t i = 0; i < 100; i++) {
+ if (tid.load() != 0) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_NE(0, tid.load());
+ ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+
+ // Wait for context data.
+ void* ucontext;
+ for (size_t i = 0; i < 2000; i++) {
+ ucontext = reinterpret_cast<void*>(g_ucontext.load());
+ if (ucontext != nullptr) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+
+ VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ g_finish = true;
+ thread.join();
+}
+
+static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
+ ASSERT_EQ(0, sigaction(signal, &act, &oldact));
+
+ OuterFunction(signal != SIGSEGV ? TEST_TYPE_REMOTE : TEST_TYPE_REMOTE_WITH_INVALID_CALL);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ if (signal != SIGSEGV) {
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_EQ(0, kill(pid, SIGUSR1));
+ }
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, remote_through_signal) {
+ RemoteThroughSignal(SIGUSR1, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
+ RemoteThroughSignal(SIGUSR1, SA_SIGINFO);
+}
+
+TEST_F(UnwindTest, remote_through_signal_with_invalid_func) {
+ RemoteThroughSignal(SIGSEGV, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo_with_invalid_func) {
+ RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
+}
+
+// Verify that using the same map while unwinding multiple threads at the
+// same time doesn't cause problems.
+TEST_F(UnwindTest, multiple_threads_unwind_same_map) {
+ static constexpr size_t kNumConcurrentThreads = 100;
+
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ auto process_memory(Memory::CreateProcessMemory(getpid()));
+
+ std::vector<std::thread*> threads;
+
+ std::atomic_bool wait;
+ wait = true;
+ size_t frames[kNumConcurrentThreads];
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() {
+ while (wait)
+ ;
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+
+ Unwinder unwinder(512, &maps, regs.get(), process_memory);
+ unwinder.Unwind();
+ frames[i] = unwinder.NumFrames();
+ ASSERT_LE(3U, frames[i]) << "Failed for thread " << i;
+ });
+ threads.push_back(thread);
+ }
+ wait = false;
+ for (auto thread : threads) {
+ thread->join();
+ delete thread;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
new file mode 100644
index 000000000..ef1950ccc
--- /dev/null
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -0,0 +1,1477 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+class UnwinderTest : public ::testing::Test {
+ protected:
+ static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name, Elf* elf = nullptr) {
+ std::string str_name(name);
+ maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+ if (elf != nullptr) {
+ const auto& map_info = *--maps_->end();
+ map_info->elf.reset(elf);
+ }
+ }
+
+ static void SetUpTestSuite() {
+ maps_.reset(new Maps);
+
+ ElfFake* elf = new ElfFake(new MemoryFake);
+ ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+ interface_fake->FakeSetBuildID("FAKE");
+ elf->FakeSetInterface(interface_fake);
+ AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
+
+ AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+
+ AddMapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/fake_device");
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so", elf);
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so", elf);
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
+
+ elf = new ElfFake(new MemoryFake);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ interface->FakeSetSoname("lib_fake.so");
+ elf->FakeSetInterface(interface);
+ AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
+ MapInfo* map_info = maps_->Find(0x43000);
+ ASSERT_TRUE(map_info != nullptr);
+ map_info->elf_start_offset = 0x1d000;
+
+ AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+
+ AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ const auto& info = *--maps_->end();
+ info->load_bias = 0;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ elf->FakeSetLoadBias(0x5000);
+ AddMapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_load_bias.so",
+ elf);
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
+ elf);
+ const auto& info2 = *--maps_->end();
+ info2->elf_offset = 0x8000;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf);
+ const auto& info3 = *--maps_->end();
+ info3->memory_backed_elf = true;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
+ const auto& info4 = *--maps_->end();
+ info4->memory_backed_elf = true;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
+ const auto& info5 = *--maps_->end();
+ info5->memory_backed_elf = true;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
+ const auto& info6 = *--maps_->end();
+ info6->memory_backed_elf = true;
+
+ AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
+ const auto& info7 = *--maps_->end();
+ info7->load_bias = 0;
+
+ process_memory_.reset(new MemoryFake);
+ }
+
+ void SetUp() override {
+ ElfInterfaceFake::FakeClear();
+ regs_.FakeSetArch(ARCH_ARM);
+ regs_.FakeSetReturnAddressValid(false);
+ }
+
+ static std::unique_ptr<Maps> maps_;
+ static RegsFake regs_;
+ static std::shared_ptr<Memory> process_memory_;
+};
+
+std::unique_ptr<Maps> UnwinderTest::maps_;
+RegsFake UnwinderTest::regs_(5);
+std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
+
+TEST_F(UnwinderTest, multiple_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x100U, frame->rel_pc);
+ EXPECT_EQ(0x1100U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("Frame2", frame->function_name);
+ EXPECT_EQ(2U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.SetResolveNames(false);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x100U, frame->rel_pc);
+ EXPECT_EQ(0x1100U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_load_bias) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xa5500);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x5500U, frame->rel_pc);
+ EXPECT_EQ(0xa5500U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xa5000U, frame->map_start);
+ EXPECT_EQ(0xa6000U, frame->map_end);
+ EXPECT_EQ(0x5000U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_elf_offset) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xa7500);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x8500U, frame->rel_pc);
+ EXPECT_EQ(0xa7500U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xa7000U, frame->map_start);
+ EXPECT_EQ(0xa8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_map_offset) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0x43000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x43000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+ EXPECT_EQ(0x43000U, frame->map_start);
+ EXPECT_EQ(0x44000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, disable_embedded_soname) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0x43000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.SetEmbeddedSoname(false);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x43000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+ EXPECT_EQ(0x43000U, frame->map_start);
+ EXPECT_EQ(0x44000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that no attempt to continue after the step indicates it is done.
+TEST_F(UnwinderTest, no_frames_after_finished) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify the maximum frames to save.
+TEST_F(UnwinderTest, max_frames) {
+ for (size_t i = 0; i < 30; i++) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
+ }
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+
+ Unwinder unwinder(20, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(20U, unwinder.NumFrames());
+
+ for (size_t i = 0; i < 20; i++) {
+ auto* frame = &unwinder.frames()[i];
+ EXPECT_EQ(i, frame->num);
+ EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
+ EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
+ EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
+ EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
+ EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
+ EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+ }
+}
+
+// Verify that initial map names frames are removed.
+TEST_F(UnwinderTest, verify_frames_skipped) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.set_pc(0x20000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
+ unwinder.Unwind(&skip_libs);
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10050U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x1000U, frame->rel_pc);
+ EXPECT_EQ(0x21000U, frame->pc);
+ EXPECT_EQ(0x10060U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x20000U, frame->map_start);
+ EXPECT_EQ(0x22000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x23000U, frame->pc);
+ EXPECT_EQ(0x10070U, frame->sp);
+ EXPECT_EQ("Frame2", frame->function_name);
+ EXPECT_EQ(2U, frame->function_offset);
+ EXPECT_EQ("/fake/libanother.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x23000U, frame->map_start);
+ EXPECT_EQ(0x24000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify SP in a non-existant map is okay.
+TEST_F(UnwinderTest, sp_not_in_map) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x63000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x63000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x1000U, frame->rel_pc);
+ EXPECT_EQ(0x21000U, frame->pc);
+ EXPECT_EQ(0x50020U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x20000U, frame->map_start);
+ EXPECT_EQ(0x22000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify PC in a device stops the unwind.
+TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.set_pc(0x13000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify SP in a device stops the unwind.
+TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x13000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify a no map info frame gets a frame.
+TEST_F(UnwinderTest, pc_without_map) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0x41000);
+ regs_.set_sp(0x13000);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x41000U, frame->rel_pc);
+ EXPECT_EQ(0x41000U, frame->pc);
+ EXPECT_EQ(0x13000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added.
+TEST_F(UnwinderTest, speculative_frame) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ // Fake as if code called a nullptr function.
+ regs_.set_pc(0);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetReturnAddress(0x1202);
+ regs_.FakeSetReturnAddressValid(true);
+
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x100U, frame->rel_pc);
+ EXPECT_EQ(0x23100U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/libanother.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x23000U, frame->map_start);
+ EXPECT_EQ(0x24000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame is added then removed because no other
+// frames are added.
+TEST_F(UnwinderTest, speculative_frame_removed) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ // Fake as if code called a nullptr function.
+ regs_.set_pc(0x20000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0x10010, false));
+ regs_.FakeSetReturnAddress(0x12);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x20000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x20000U, frame->map_start);
+ EXPECT_EQ(0x22000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added and left if there are only
+// two frames and the pc is in the middle nowhere.
+TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ // Fake as if code called a nullptr function.
+ regs_.set_pc(0);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetReturnAddress(0x1202);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame does not cause a crash when it wasn't
+// really added due to a filter.
+TEST_F(UnwinderTest, speculative_frame_check_with_no_frames) {
+ regs_.set_pc(0x23000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetReturnAddress(0x23100);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+
+ std::vector<std::string> skip_names{"libanother.so"};
+ unwinder.Unwind(&skip_names);
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(0U, unwinder.NumFrames());
+}
+
+// Verify that an unwind stops when a frame is in given suffix.
+TEST_F(UnwinderTest, map_ignore_suffixes) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+
+ // Fake as if code called a nullptr function.
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x43402, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ std::vector<std::string> suffixes{"oat"};
+ unwinder.Unwind(nullptr, &suffixes);
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+ // Make sure the elf was not initialized.
+ MapInfo* map_info = maps_->Find(0x53000);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_TRUE(map_info->elf == nullptr);
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0x43400U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+ EXPECT_EQ(0x43000U, frame->map_start);
+ EXPECT_EQ(0x44000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that an unwind stops when the sp and pc don't change.
+TEST_F(UnwinderTest, sp_pc_do_not_change) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0x33400U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/compressed.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x33000U, frame->map_start);
+ EXPECT_EQ(0x34000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x500U, frame->rel_pc);
+ EXPECT_EQ(0x33500U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("Frame2", frame->function_name);
+ EXPECT_EQ(2U, frame->function_offset);
+ EXPECT_EQ("/fake/compressed.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x33000U, frame->map_start);
+ EXPECT_EQ(0x34000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_in_map) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xa3400);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xa3400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xa3000U, frame->map_start);
+ EXPECT_EQ(0xa4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_in_map_non_zero_offset) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xd0400);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xd0400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk", frame->map_name);
+ EXPECT_EQ(0x1000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1000U, frame->map_exact_offset);
+ EXPECT_EQ(0xd0000U, frame->map_start);
+ EXPECT_EQ(0xd1000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_not_in_map) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0x50000);
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50000U, frame->rel_pc);
+ EXPECT_EQ(0x50000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_multiple_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xa3400);
+ ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xa3400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xa3000U, frame->map_start);
+ EXPECT_EQ(0xa4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0x33400U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/compressed.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x33000U, frame->map_start);
+ EXPECT_EQ(0x34000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_max_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xa3400);
+
+ Unwinder unwinder(1, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xa3400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xa3000U, frame->map_start);
+ EXPECT_EQ(0xa4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_not_file) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc0050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_TRUE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc0050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/unreadable.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc0000U, frame->map_start);
+ EXPECT_EQ(0xc1000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc1050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc1050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("[vdso]", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc1000U, frame->map_start);
+ EXPECT_EQ(0xc2000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc2050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc2050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc2000U, frame->map_start);
+ EXPECT_EQ(0xc3000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc3050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc3050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc3000U, frame->map_start);
+ EXPECT_EQ(0xc4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame) {
+ RegsFake regs_arm(10);
+ regs_arm.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder32(10, maps_.get(), &regs_arm, process_memory_);
+
+ RegsFake regs_arm64(10);
+ regs_arm64.FakeSetArch(ARCH_ARM64);
+ Unwinder unwinder64(10, maps_.get(), &regs_arm64, process_memory_);
+
+ FrameData frame;
+ frame.num = 1;
+ frame.rel_pc = 0x1000;
+ frame.pc = 0x4000;
+ frame.sp = 0x1000;
+ frame.function_name = "function";
+ frame.function_offset = 100;
+ frame.map_name = "/fake/libfake.so";
+ frame.map_elf_start_offset = 0x2000;
+ frame.map_start = 0x3000;
+ frame.map_end = 0x6000;
+ frame.map_flags = PROT_READ;
+
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)",
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)",
+ unwinder32.FormatFrame(frame));
+
+ frame.map_elf_start_offset = 0;
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
+
+ frame.function_offset = 0;
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function)",
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", unwinder32.FormatFrame(frame));
+
+ // Verify the function name is demangled.
+ frame.function_name = "_ZN4funcEv";
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (func())", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (func())", unwinder32.FormatFrame(frame));
+
+ frame.function_name = "";
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", unwinder32.FormatFrame(frame));
+
+ frame.map_name = "";
+ EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", unwinder32.FormatFrame(frame));
+
+ frame.map_start = 0;
+ frame.map_end = 0;
+ EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 <unknown>", unwinder32.FormatFrame(frame));
+}
+
+TEST_F(UnwinderTest, format_frame_build_id) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
+
+ FrameData frame;
+ frame.num = 1;
+ frame.rel_pc = 0x1000;
+ frame.pc = 0x4000;
+ frame.sp = 0x1000;
+ frame.function_name = "function";
+ frame.function_offset = 100;
+ frame.map_name = "/fake/libfake.so";
+ frame.map_elf_start_offset = 0;
+ frame.map_start = 0x3000;
+ frame.map_end = 0x6000;
+ frame.map_flags = PROT_READ;
+
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
+ unwinder.SetDisplayBuildID(true);
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100) (BuildId: 46414b45)",
+ unwinder.FormatFrame(frame));
+}
+
+static std::string ArchToString(ArchEnum arch) {
+ if (arch == ARCH_ARM) {
+ return "Arm";
+ } else if (arch == ARCH_ARM64) {
+ return "Arm64";
+ } else if (arch == ARCH_X86) {
+ return "X86";
+ } else if (arch == ARCH_X86_64) {
+ return "X86_64";
+ } else {
+ return "Unknown";
+ }
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame_by_arch) {
+ std::vector<Regs*> reg_list;
+ RegsArm* arm = new RegsArm;
+ arm->set_pc(0x2300);
+ arm->set_sp(0x10000);
+ reg_list.push_back(arm);
+
+ RegsArm64* arm64 = new RegsArm64;
+ arm64->set_pc(0x2300);
+ arm64->set_sp(0x10000);
+ reg_list.push_back(arm64);
+
+ RegsX86* x86 = new RegsX86;
+ x86->set_pc(0x2300);
+ x86->set_sp(0x10000);
+ reg_list.push_back(x86);
+
+ RegsX86_64* x86_64 = new RegsX86_64;
+ x86_64->set_pc(0x2300);
+ x86_64->set_sp(0x10000);
+ reg_list.push_back(x86_64);
+
+ RegsMips* mips = new RegsMips;
+ mips->set_pc(0x2300);
+ mips->set_sp(0x10000);
+ reg_list.push_back(mips);
+
+ RegsMips64* mips64 = new RegsMips64;
+ mips64->set_pc(0x2300);
+ mips64->set_sp(0x10000);
+ reg_list.push_back(mips64);
+
+ for (auto regs : reg_list) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+
+ Unwinder unwinder(64, maps_.get(), regs, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+ std::string expected;
+ switch (regs->Arch()) {
+ case ARCH_ARM:
+ case ARCH_X86:
+ case ARCH_MIPS:
+ expected = " #00 pc 00001300 /system/fake/libc.so (Frame0+10)";
+ break;
+ case ARCH_ARM64:
+ case ARCH_X86_64:
+ case ARCH_MIPS64:
+ expected = " #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)";
+ break;
+ default:
+ expected = "";
+ }
+ EXPECT_EQ(expected, unwinder.FormatFrame(0))
+ << "Mismatch of frame format for regs arch " << ArchToString(regs->Arch());
+ delete regs;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
new file mode 100644
index 000000000..6a3e91a2a
--- /dev/null
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+
+#if defined(__BIONIC__)
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+// This test is specific to bionic to verify that __libc_init is
+// properly setting the return address to undefined so that the
+// unwind properly terminates.
+
+namespace unwindstack {
+
+static std::string DumpFrames(const UnwinderFromPid& unwinder) {
+ std::string unwind;
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ unwind += unwinder.FormatFrame(i) + '\n';
+ }
+ return unwind;
+}
+
+static DwarfLocationEnum GetReturnAddressLocation(uint64_t rel_pc, DwarfSection* section) {
+ if (section == nullptr) {
+ return DWARF_LOCATION_INVALID;
+ }
+
+ const DwarfFde* fde = section->GetFdeFromPc(rel_pc);
+ if (fde == nullptr || fde->cie == nullptr) {
+ return DWARF_LOCATION_INVALID;
+ }
+ dwarf_loc_regs_t regs;
+ if (!section->GetCfaLocationInfo(rel_pc, fde, &regs)) {
+ return DWARF_LOCATION_INVALID;
+ }
+
+ auto reg_entry = regs.find(fde->cie->return_address_register);
+ if (reg_entry == regs.end()) {
+ return DWARF_LOCATION_INVALID;
+ }
+ return reg_entry->second.type;
+}
+
+static void VerifyReturnAddress(const FrameData& frame) {
+ // Now go and find information about the register data and verify that the relative pc results in
+ // an undefined register.
+ Elf elf(Memory::CreateFileMemory(frame.map_name, 0).release());
+ ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_name;
+ ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_name << " is not valid.";
+ ElfInterface* interface = elf.interface();
+
+ // Only check the eh_frame and the debug_frame since the undefined register
+ // is set using a cfi directive.
+ // Check debug_frame first, then eh_frame since debug_frame always
+ // contains the most specific data.
+ DwarfLocationEnum location = GetReturnAddressLocation(frame.rel_pc, interface->debug_frame());
+ if (location == DWARF_LOCATION_UNDEFINED) {
+ return;
+ }
+
+ location = GetReturnAddressLocation(frame.rel_pc, interface->eh_frame());
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location);
+}
+
+// This test assumes that it starts from the main thread, and that the
+// libc.so on device will include symbols so that function names can
+// be resolved.
+TEST(VerifyBionicTermination, local_terminate) {
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+
+ UnwinderFromPid unwinder(512, getpid());
+ ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ unwinder.SetRegs(regs.get());
+
+ RegsGetLocal(regs.get());
+ unwinder.Unwind();
+ ASSERT_LT(0U, unwinder.NumFrames());
+
+ SCOPED_TRACE(DumpFrames(unwinder));
+
+ // Look for the frame that includes __libc_init, there should only
+ // be one and it should be the last.
+ bool found = false;
+ const std::vector<FrameData>& frames = unwinder.frames();
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ const FrameData& frame = frames[i];
+ if (frame.function_name == "__libc_init" && !frame.map_name.empty() &&
+ std::string("libc.so") == basename(frame.map_name.c_str())) {
+ ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
+ ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found) << "Unable to find libc.so:__libc_init frame\n";
+}
+
+} // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tests/files/elf32.xz b/libunwindstack/tests/files/elf32.xz
new file mode 100644
index 000000000..f25d43349
--- /dev/null
+++ b/libunwindstack/tests/files/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/files/elf64.xz b/libunwindstack/tests/files/elf64.xz
new file mode 100644
index 000000000..eb1618e7e
--- /dev/null
+++ b/libunwindstack/tests/files/elf64.xz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
new file mode 100644
index 000000000..300646b7b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
new file mode 100644
index 000000000..999cb790a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
new file mode 100644
index 000000000..6aa1c824f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
new file mode 100644
index 000000000..19d7b65cf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
new file mode 100644
index 000000000..edcd3e169
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
new file mode 100644
index 000000000..09ba49532
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
new file mode 100644
index 000000000..39c9025db
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
new file mode 100644
index 000000000..1ff12db1e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -0,0 +1,4 @@
+d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
+e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e4af1000-e4af2000 rw-p 482000 00:00 0 libart.so
+e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
new file mode 100644
index 000000000..0b518143a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: e814103c
+r1: 12dcf218
+r2: 1a90df75
+r3: ffffffbf
+r4: 0
+r5: 12dc0800
+r6: 12dcf218
+r7: 1a90df75
+r8: 0
+r9: dd23cc00
+r10: 1c
+r11: cd4ff16c
+ip: 0
+sp: cd4ff140
+lr: d025cdd7
+pc: d025c788
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
new file mode 100644
index 000000000..f00917b77
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
new file mode 100644
index 000000000..78449bf8e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
new file mode 100644
index 000000000..7cada1505
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
@@ -0,0 +1,2 @@
+60a9fdf000-60a9fe0000 r-xp 0 00:00 0 waiter64
+7542cc0000-7542d8e000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
new file mode 100644
index 000000000..c24adbe64
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 60a9fdf550
+sp: 7fdd141990
+lr: 60a9fdf56c
+x29: 7fdd1419a0
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
new file mode 100644
index 000000000..b56d42008
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64 b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
new file mode 100644
index 000000000..81bda1d2e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
new file mode 100644
index 000000000..9c787902b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt b/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
new file mode 100644
index 000000000..74fc89f6d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
@@ -0,0 +1,2 @@
+56598000-56599000 r-xp 0 00:00 0 waiter
+f7432000-f75e3000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt b/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
new file mode 100644
index 000000000..48f44401c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: 1d88ef8c
+ebx: 56599fe8
+ecx: 3
+edx: ffcf9ea4
+ebp: ffcf9e48
+edi: f75e5000
+esi: 1
+esp: ffcf9e38
+eip: 56598685
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data b/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
new file mode 100644
index 000000000..0cf7d55a8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
new file mode 100644
index 000000000..b1fc0241b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
new file mode 100644
index 000000000..4b7bf44c4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
new file mode 100644
index 000000000..013858ef5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
new file mode 100644
index 000000000..10f13254b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
@@ -0,0 +1,3 @@
+8d1c000-8d1f000 r-xp 0 00:00 0 mediaserver
+f0b91000-f0c2c000 r-xp 0 00:00 0 libc.so
+f1a41000-f1a97000 r-xp 0 00:00 0 libbinder.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
new file mode 100644
index 000000000..9e4a83f01
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
new file mode 100644
index 000000000..f147247e5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 3
+r1: c0306201
+r2: ffd4a658
+r3: 0
+r4: f0c36d8c
+r5: ffd4a658
+r6: f0168000
+r7: 36
+r8: ffd4a678
+r9: f016802c
+r10: ffd4a660
+r11: 0
+ip: 0
+sp: ffd4a638
+lr: f0bb2413
+pc: f0be238c
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
new file mode 100644
index 000000000..847c8192a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
new file mode 100644
index 000000000..f3eb6153d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt b/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
new file mode 100644
index 000000000..7d5248393
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
@@ -0,0 +1,3 @@
+eb503000-eb5e8000 r-xp 0 00:00 0 libc.so
+eb831000-eb852000 r-xp 0 00:00 0 tombstoned
+ffffe000-fffff000 r-xp 0 00:00 0 vdso.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt b/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
new file mode 100644
index 000000000..821928e79
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: fffffffc
+ebx: 4
+ecx: eb290180
+edx: 20
+ebp: 8
+edi: 0
+esi: ffffffff
+esp: fffe1a30
+eip: ffffe430
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data b/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
new file mode 100644
index 000000000..b95bfac9e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
new file mode 100644
index 000000000..aefdb6b4c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
new file mode 100644
index 000000000..c71dcfbe5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
new file mode 100644
index 000000000..46b6f4560
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
new file mode 100644
index 000000000..ac2e56432
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
@@ -0,0 +1,2 @@
+561550b17000-561550b1a000 r-xp 0 00:00 0 unwind_test64
+7f4de61f6000-7f4de638b000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
new file mode 100644
index 000000000..38af274bb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
@@ -0,0 +1,11 @@
+rax: 92134c6fbbdc12ff
+rbx: 0
+rcx: 92134c6fbbdc1200
+rdx: 92134c6fbbdc1200
+r8: 561552153034
+r12: 561550b17930
+r13: 7ffcc8597270
+rsi: 561552153034
+rbp: 7ffcc8596f30
+rsp: 7ffcc8596ce8
+rip: 561550b17a80
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
new file mode 100644
index 000000000..cc7882b83
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64 b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
new file mode 100644
index 000000000..ab0ef8f70
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
new file mode 100644
index 000000000..f30384cd0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libc.so b/libunwindstack/tests/files/offline/empty_arm64/libc.so
new file mode 100644
index 000000000..b05dcafde
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/maps.txt b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
new file mode 100644
index 000000000..edb83c6d1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/maps.txt
@@ -0,0 +1,9 @@
+5d4786b000-5d47893000 r--p 0 00:00 0 netd
+5d47893000-5d47894000 ---p 0 00:00 0
+5d47894000-5d47901000 --xp 29000 00:00 0 netd
+729f709000-729f750000 r--p 0 00:00 0 libbinder.so
+729f750000-729f751000 ---p 0 00:00 0
+729f751000-729f794000 --xp 48000 00:00 0 libbinder.so
+72a018a000-72a01c2000 r--p 0 00:00 0 libc.so
+72a01c2000-72a01c3000 ---p 0 00:00 0
+72a01c3000-72a023b000 --xp 39000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/empty_arm64/netd b/libunwindstack/tests/files/offline/empty_arm64/netd
new file mode 100644
index 000000000..8a72e9491
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/netd
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/regs.txt b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
new file mode 100644
index 000000000..3d4279f7a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 1d
+x1: c0306201
+x2: 7ffb6c0c50
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 0
+x8: 1d
+x9: 7ffb6c0c00
+x10: 7ffb6c0c50
+x11: 7ffb6c0bd0
+x12: ffffff80ffffffd0
+x13: 0
+x14: 72a0240ce2
+x15: 20
+x16: 729f7a54e8
+x17: 72a01dd3c0
+x18: 72a0ac2000
+x19: 72a0666000
+x20: 719769b610
+x21: 719769b730
+x22: c0306201
+x23: fffffff7
+x24: 72a0666000
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7ffb6c0c30
+sp: 7ffb6c0b50
+lr: 72a01dd450
+pc: 72a02203a4
+pst: a0000000
diff --git a/libunwindstack/tests/files/offline/empty_arm64/stack.data b/libunwindstack/tests/files/offline/empty_arm64/stack.data
new file mode 100644
index 000000000..6d6108c60
--- /dev/null
+++ b/libunwindstack/tests/files/offline/empty_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
new file mode 100644
index 000000000..e4283e63b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
new file mode 100644
index 000000000..1bcddb6b8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
@@ -0,0 +1 @@
+f1f10000-f2049000 r-xp 00000000 00:00 0 libandroid_runtime.so
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
new file mode 100644
index 000000000..c6a93dce5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
@@ -0,0 +1,2 @@
+pc: f1f6dc49
+sp: d8fe6930
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data b/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
new file mode 100644
index 000000000..19cdf2ddf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
new file mode 100644
index 000000000..022404c77
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
@@ -0,0 +1 @@
+c7ee8000-c8c52fff r-xp 12e4000 00:00 0 invalid.apk
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
new file mode 100644
index 000000000..b7f10efc1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: c0434c00
+r1: 2a4c9fbc
+r2: 00000000
+r3: c83ef1f9
+r4: 00000004
+r5: c2044904
+r6: 00000000
+r7: c20443b8
+r8: 000b33ff
+r9: c20444b0
+r10: cac90740
+r11: 00000000
+ip: ed891ca4
+sp: c2044218
+lr: ed807265
+pc: c898f508
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
new file mode 100644
index 000000000..35a6bc5ab
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
new file mode 100644
index 000000000..def299e01
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
new file mode 100644
index 000000000..7b876b5fc
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
new file mode 100644
index 000000000..3c468d641
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
new file mode 100644
index 000000000..2c7689b3c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
new file mode 100644
index 000000000..22a35b82b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
new file mode 100644
index 000000000..61f39275e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
new file mode 100644
index 000000000..1a37628e4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
new file mode 100644
index 000000000..7ef62caef
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
new file mode 100644
index 000000000..6d27c890b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
new file mode 100644
index 000000000..bfbceeabd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
new file mode 100644
index 000000000..b78848e9a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
new file mode 100644
index 000000000..8f927ac79
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
new file mode 100644
index 000000000..1d1dfca8c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
new file mode 100644
index 000000000..89aeb43c1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
new file mode 100644
index 000000000..e076934c5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
new file mode 100644
index 000000000..17d604157
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
new file mode 100644
index 000000000..aaff0378c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libart.so b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
new file mode 100644
index 000000000..05278936b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
new file mode 100644
index 000000000..855905655
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
new file mode 100644
index 000000000..06dbf10c7
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libc.so b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
new file mode 100644
index 000000000..9894e6627
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
new file mode 100644
index 000000000..3b87f2f62
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -0,0 +1,11 @@
+ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32
+dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
+e0445000-e0447000 r--p 0 00:00 0 137-cfi.odex
+e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
+e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
+e648e000-e690f000 r-xp 0 00:00 0 libart.so
+e6918000-e6919000 rw-p 489000 00:00 0 libart.so
+ed306000-ed801000 r-xp 0 00:00 0 libartd.so
+ed80a000-ed80b000 rw-p 503000 00:00 0 libartd.so
+eda88000-edb23000 r-xp 0 00:00 0 libc.so
+ede4e000-ede50000 r-xp 0 00:00 0 anonymous:ede4e000
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt b/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
new file mode 100644
index 000000000..0e20066ed
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: dfe7c0f8
+r1: 0
+r2: 0
+r3: 40000000
+r4: e051ffb4
+r5: 0
+r6: e051ffc0
+r7: ede514e8
+r8: ff85d1a8
+r9: ed9210c0
+r10: 58
+r11: 0
+ip: edb26d04
+sp: ff85d180
+lr: edaff5af
+pc: dfe66a5e
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/stack.data b/libunwindstack/tests/files/offline/jit_debug_arm/stack.data
new file mode 100644
index 000000000..b2ff14eb9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
new file mode 100644
index 000000000..870ac0a74
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
new file mode 100644
index 000000000..76ffad93e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
new file mode 100644
index 000000000..466dae28b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
new file mode 100644
index 000000000..3a725e800
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
new file mode 100644
index 000000000..767550f00
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
new file mode 100644
index 000000000..e7e492e79
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
new file mode 100644
index 000000000..65f9cd432
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
new file mode 100644
index 000000000..30aa28cfb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
new file mode 100644
index 000000000..3c89673f2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
new file mode 100644
index 000000000..9c9b83c3c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
new file mode 100644
index 000000000..eaad1420a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
new file mode 100644
index 000000000..d53481644
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
new file mode 100644
index 000000000..dbeb886cc
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
new file mode 100644
index 000000000..bf2142d82
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
new file mode 100644
index 000000000..e2ba1b0f2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
new file mode 100644
index 000000000..c27ba541c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
new file mode 100644
index 000000000..5fc8fae46
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
new file mode 100644
index 000000000..92ed9915d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
new file mode 100644
index 000000000..5efae0273
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
new file mode 100644
index 000000000..9c787902b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
new file mode 100644
index 000000000..c22b5deaf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -0,0 +1,8 @@
+56573000-56577000 r-xp 0 00:00 0 dalvikvm32
+eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
+ec604000-ec606000 r--p 0 00:00 0 137-cfi.odex
+ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
+ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
+f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
+f7334000-f7335000 rw-p 752000 00:00 0 libartd.so
+f734b000-f74fc000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
new file mode 100644
index 000000000..f68305b81
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: eb8cccd0
+ebx: eb8cccd0
+ecx: ff
+edx: ffeb2ca8
+ebp: ffeb5298
+edi: ffeb5c08
+esi: ffeb5c00
+esp: ffeb5280
+eip: eb89bfb8
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86/stack.data
new file mode 100644
index 000000000..c3457624e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
new file mode 100644
index 000000000..e66788377
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
new file mode 100644
index 000000000..9a1d71483
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libart.so b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
new file mode 100644
index 000000000..09ba49532
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libc.so b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
new file mode 100644
index 000000000..39c9025db
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/maps.txt b/libunwindstack/tests/files/offline/jit_map_arm/maps.txt
new file mode 100644
index 000000000..5aaec5420
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/maps.txt
@@ -0,0 +1,2 @@
+e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/regs.txt b/libunwindstack/tests/files/offline/jit_map_arm/regs.txt
new file mode 100644
index 000000000..0b518143a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: e814103c
+r1: 12dcf218
+r2: 1a90df75
+r3: ffffffbf
+r4: 0
+r5: 12dc0800
+r6: 12dcf218
+r7: 1a90df75
+r8: 0
+r9: dd23cc00
+r10: 1c
+r11: cd4ff16c
+ip: 0
+sp: cd4ff140
+lr: d025cdd7
+pc: d025c788
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/stack.data b/libunwindstack/tests/files/offline/jit_map_arm/stack.data
new file mode 100644
index 000000000..fb8feebf1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
new file mode 100644
index 000000000..7bb7156bb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64 b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
new file mode 100644
index 000000000..00a3896d6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
new file mode 100644
index 000000000..a2babee1c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
@@ -0,0 +1,7 @@
+5f73997000-5f739dc000 r--p 0 00:00 0 test
+5f739dc000-5f73a43000 r-xp 44000 00:00 0 test
+711152c000-711156e000 r--p 0 00:00 0 libc.so
+711156e000-7111611000 --xp 42000 00:00 0 libc.so
+7112be2000-7112be4000 r-xp 0 00:00 0 vdso
+7112be4000-7112c1c000 r--p 0 00:00 0 linker64
+7112c1c000-7112ce1000 r-xp 38000 00:00 0 linker64
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
new file mode 100644
index 000000000..3c601e15e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7112bdbc24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: a78826643b37f4a1
+x10: 7112bdbc20
+x11: 4100
+x12: 7112bdbb70
+x13: 18
+x14: 1d6518077
+x15: 2a43148faf732a
+x16: 16fc0
+x17: 71115f61a0
+x18: 7111d6a000
+x19: 7112cef1b0
+x20: 7112bdbda0
+x21: 59616d61
+x22: 1
+x23: 7112bdbc24
+x24: 4b0e
+x25: 62
+x26: 2
+x27: 0
+x28: 7111934020
+x29: 7112bdbd90
+sp: 7112bdbbf0
+lr: 7112c394ec
+pc: 7112cb99bc
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
new file mode 100644
index 000000000..167473373
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
new file mode 100644
index 000000000..6d7b48ab1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
new file mode 100644
index 000000000..3a75b8fbe
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
new file mode 100644
index 000000000..49409167f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
new file mode 100644
index 000000000..63383d043
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
new file mode 100644
index 000000000..ba5a31bac
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
@@ -0,0 +1,3 @@
+200000-919000 r--p 0 00:00 0 perfetto_unittests
+919000-1a0c000 r-xp 719000 00:00 0 perfetto_unittests
+7f932696e000-7f9326b23000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
new file mode 100644
index 000000000..a30e5999f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
new file mode 100644
index 000000000..6cb405516
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
@@ -0,0 +1,17 @@
+rax: 3b
+rbx: 3b
+rcx: 7f9326a57dd4
+rdx: 3b
+r8: 7ffd22415b09
+r9: 7ffd224155e0
+r10: 0
+r11: 246
+r12: 7f9326d28760
+r13: 3b
+r14: 7f9326d23760
+r15: 3b
+rdi: 1
+rsi: 2678850
+rbp: 2678850
+rsp: 7ffd224153c8
+rip: 7f9326a57dd4
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
new file mode 100644
index 000000000..4edfe0751
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
new file mode 100644
index 000000000..9f5c8ca44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
new file mode 100644
index 000000000..7a30bfa44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
new file mode 100644
index 000000000..768dd9f8d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -0,0 +1,4 @@
+2b2a000-2b6c000 r--p 0 00:00 0 libunwindstack_test
+2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
+f4110000-f4135000 r--p 0 00:00 0 libc.so
+f4135000-f41a9000 r-xp 25000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt
new file mode 100644
index 000000000..1f4ac8f7b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 5
+r1: 5
+r2: 4
+r3: 1
+r4: 73804b6b
+r5: f3c9c000
+r6: 2ea09ac
+r7: 10624dd3
+r8: f41b5d8c
+r9: f3c9c000
+r10: 6f17
+r11: f3c94048
+ip: 2ea0807
+sp: f43d2ccc
+lr: 2e55fef
+pc: 2e55fa0
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data
new file mode 100644
index 000000000..23a987415
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data
new file mode 100644
index 000000000..49bdd1e26
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
new file mode 100644
index 000000000..02773599b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
new file mode 100644
index 000000000..20008fd15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
new file mode 100644
index 000000000..b90933ba6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
new file mode 100644
index 000000000..c4fc06764
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 4000 00:00 0 ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 40000 00:00 0 ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0 libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0 libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0 vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0 linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0 linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
new file mode 100644
index 000000000..1e2ea3206
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
new file mode 100644
index 000000000..ec07e15c5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
new file mode 100644
index 000000000..825bb1aa5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
new file mode 100644
index 000000000..205ebd43a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
new file mode 100644
index 000000000..f39d127e0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
new file mode 100644
index 000000000..20008fd15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
new file mode 100644
index 000000000..b90933ba6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
new file mode 100644
index 000000000..386d57a24
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 21d5000 00:00 0 ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 2211000 00:00 0 ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0 libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0 libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0 vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0 linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0 linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
new file mode 100644
index 000000000..1e2ea3206
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
new file mode 100644
index 000000000..ec07e15c5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
new file mode 100644
index 000000000..825bb1aa5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
new file mode 100644
index 000000000..205ebd43a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
new file mode 100644
index 000000000..cac1dd9b8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
new file mode 100644
index 000000000..2c5ca62e1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
@@ -0,0 +1,3 @@
+7be5e48000-7be6b2b000 r-xp 5000 00:00 0 test.apk
+7cbe030000-7cbe070000 r--p 0 00:00 0 libc.so
+7cbe070000-7cbe11a000 r-xp 40000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
new file mode 100644
index 000000000..090aedae6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7c326f6568
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 1
+x10: 1
+x11: 0
+x12: ffffffffc4653600
+x13: 17645696f
+x14: 2742ed97ca77a3
+x15: 3ab49084
+x16: 7be6b6bdb8
+x17: 7cbe0b14a0
+x18: 7c2b02a000
+x19: 0
+x20: 7c326f6568
+x21: 7be69c827c
+x22: 7be69c8272
+x23: 1
+x24: 7be74f7100
+x25: 881
+x26: 7be4f07a00
+x27: c479c000
+x28: 7be4f07998
+x29: 7be4f079b4
+sp: 7be4f077d0
+lr: 7be6715f60
+pc: 7cbe0b14bc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
new file mode 100644
index 000000000..27d5bf34b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
new file mode 100644
index 000000000..70a9c71ea
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
new file mode 100644
index 000000000..f046624f6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test b/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
new file mode 100644
index 000000000..f460dd673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt
new file mode 100644
index 000000000..165ae4964
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt
@@ -0,0 +1,4 @@
+b66b7000-b670c000 r--p 0 00:00 0 libunwindstack_unit_test
+b670c000-b69a8000 r-xp 54000 00:00 0 libunwindstack_unit_test
+f23a6000-f23d0000 r--p 0 00:00 0 libc.so
+f23d0000-f2451000 r-xp 29000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt
new file mode 100644
index 000000000..e03f8fd81
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: b69b7c84
+r1: 1
+r2: 1
+r3: 1
+r4: f1e52bd0
+r5: f1e11000
+r6: f1e52bd0
+r7: f1e52a38
+r8: f1e11000
+r9: 5de82a8f
+r10: f1e06030
+r11: f1e6d080
+ip: ffe67a88
+sp: f2790ce8
+lr: b6955fab
+pc: b6955f9e
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data
new file mode 100644
index 000000000..d9f23f8e9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data
new file mode 100644
index 000000000..6011883e3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libbase.so b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
new file mode 100644
index 000000000..d1f16ee8a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libc.so b/libunwindstack/tests/files/offline/straddle_arm/libc.so
new file mode 100644
index 000000000..4dc19ca6c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/maps.txt b/libunwindstack/tests/files/offline/straddle_arm/maps.txt
new file mode 100644
index 000000000..8c26479fa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/maps.txt
@@ -0,0 +1,4 @@
+f2d9a000-f2da7fff r-xp 00000000 00:00 0 libbase.so
+f3002000-f3005fff rw-p 00000000 00:00 0 [stack:25941]
+f31d0000-f326bfff r-xp 00000000 00:00 0 libc.so
+f3352000-f336bfff r-xp 00000000 00:00 0 /does/not/exist/libhidlbase.so
diff --git a/libunwindstack/tests/files/offline/straddle_arm/regs.txt b/libunwindstack/tests/files/offline/straddle_arm/regs.txt
new file mode 100644
index 000000000..3baedf365
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/regs.txt
@@ -0,0 +1,3 @@
+pc: f31ea9f8
+sp: e9c866f8
+lr: f31f179f
diff --git a/libunwindstack/tests/files/offline/straddle_arm/stack.data b/libunwindstack/tests/files/offline/straddle_arm/stack.data
new file mode 100644
index 000000000..83aeb4aaa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
new file mode 100644
index 000000000..092fc3aec
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
new file mode 100644
index 000000000..bdf29b5fe
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
@@ -0,0 +1,2 @@
+00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0 libunwindstack_test
+0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0 [stack]
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
new file mode 100644
index 000000000..ff8a936d2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 00000064d09d4fd8
+sp: 0000007fe0d84040
+lr: 00000064d09d507c
+x29: 0000007fe0d84070
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
new file mode 100644
index 000000000..824d0e2c3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 000000000..1812e5037
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+ return false;
+ }
+
+ // Allow at least 1 second to attach properly.
+ for (size_t i = 0; i < 1000; i++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return true;
+ }
+ usleep(1000);
+ }
+ printf("%d: Failed to stop.\n", pid);
+ return false;
+}
+
+void DoUnwind(pid_t pid) {
+ unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+ if (regs == nullptr) {
+ printf("Unable to get remote reg data\n");
+ return;
+ }
+
+ printf("ABI: ");
+ switch (regs->Arch()) {
+ case unwindstack::ARCH_ARM:
+ printf("arm");
+ break;
+ case unwindstack::ARCH_X86:
+ printf("x86");
+ break;
+ case unwindstack::ARCH_ARM64:
+ printf("arm64");
+ break;
+ case unwindstack::ARCH_X86_64:
+ printf("x86_64");
+ break;
+ case unwindstack::ARCH_MIPS:
+ printf("mips");
+ break;
+ case unwindstack::ARCH_MIPS64:
+ printf("mips64");
+ break;
+ default:
+ printf("unknown\n");
+ return;
+ }
+ printf("\n");
+
+ unwindstack::UnwinderFromPid unwinder(1024, pid);
+ if (!unwinder.Init(regs->Arch())) {
+ printf("Failed to init unwinder object.\n");
+ return;
+ }
+
+ unwinder.SetRegs(regs);
+ unwinder.Unwind();
+
+ // Print the frames.
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ printf("%s\n", unwinder.FormatFrame(i).c_str());
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Usage: unwind <PID>\n");
+ return 1;
+ }
+
+ pid_t pid = atoi(argv[1]);
+ if (!Attach(pid)) {
+ printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+ return 1;
+ }
+
+ DoUnwind(pid);
+
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+
+ return 0;
+}
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
new file mode 100644
index 000000000..64b58a83a
--- /dev/null
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include <android-base/stringprintf.h>
+
+struct map_info_t {
+ uint64_t start;
+ uint64_t end;
+ uint64_t offset;
+ uint64_t flags;
+ std::string name;
+};
+
+static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+ return false;
+ }
+
+ // Allow at least 1 second to attach properly.
+ for (size_t i = 0; i < 1000; i++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return true;
+ }
+ usleep(1000);
+ }
+ printf("%d: Failed to stop.\n", pid);
+ return false;
+}
+
+bool SaveRegs(unwindstack::Regs* regs) {
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "w+"), &fclose);
+ if (fp == nullptr) {
+ perror("Failed to create file regs.txt");
+ return false;
+ }
+ regs->IterateRegisters([&fp](const char* name, uint64_t value) {
+ fprintf(fp.get(), "%s: %" PRIx64 "\n", name, value);
+ });
+
+ return true;
+}
+
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+ for (size_t i = 0; i < stacks.size(); i++) {
+ std::string file_name;
+ if (stacks.size() != 1) {
+ file_name = "stack" + std::to_string(i) + ".data";
+ } else {
+ file_name = "stack.data";
+ }
+
+ // Do this first, so if it fails, we don't create the file.
+ uint64_t sp_start = stacks[i].first;
+ uint64_t sp_end = stacks[i].second;
+ std::vector<uint8_t> buffer(sp_end - sp_start);
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+ printf("Unable to read stack data.\n");
+ return false;
+ }
+
+ printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
+ if (fp == nullptr) {
+ perror("Failed to create stack.data");
+ return false;
+ }
+
+ size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+ if (bytes != sizeof(sp_start)) {
+ printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
+ bytes);
+ return false;
+ }
+
+ bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+ if (bytes != buffer.size()) {
+ printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
+ std::string cur_name;
+ if (info->name.empty()) {
+ cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
+ } else {
+ cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
+ }
+
+ std::vector<uint8_t> buffer(info->end - info->start);
+ // If this is a mapped in file, it might not be possible to read the entire
+ // map, so read all that is readable.
+ size_t bytes = memory->Read(info->start, buffer.data(), buffer.size());
+ if (bytes == 0) {
+ printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
+ return false;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+ if (output == nullptr) {
+ perror((std::string("Cannot create ") + cur_name).c_str());
+ return false;
+ }
+
+ size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
+ if (bytes_written != bytes) {
+ printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
+ return false;
+ }
+
+ // Replace the name with the new name.
+ info->name = cur_name;
+
+ return true;
+}
+
+bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
+ std::string cur_name = basename(info->name.c_str());
+ if (*file_copied) {
+ info->name = cur_name;
+ return true;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
+ if (fp == nullptr) {
+ perror((std::string("Cannot open ") + info->name).c_str());
+ return false;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+ if (output == nullptr) {
+ perror((std::string("Cannot create file " + cur_name)).c_str());
+ return false;
+ }
+ std::vector<uint8_t> buffer(10000);
+ size_t bytes;
+ while ((bytes = fread(buffer.data(), 1, buffer.size(), fp.get())) > 0) {
+ size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
+ if (bytes_written != bytes) {
+ printf("Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
+ bytes_written);
+ return false;
+ }
+ }
+
+ // Replace the name with the new name.
+ info->name = cur_name;
+
+ return true;
+}
+
+map_info_t* FillInAndGetMapInfo(std::unordered_map<uint64_t, map_info_t>& maps_by_start,
+ unwindstack::MapInfo* map_info) {
+ auto info = &maps_by_start[map_info->start];
+ info->start = map_info->start;
+ info->end = map_info->end;
+ info->offset = map_info->offset;
+ info->name = map_info->name;
+ info->flags = map_info->flags;
+
+ return info;
+}
+
+void SaveMapInformation(std::shared_ptr<unwindstack::Memory>& process_memory, map_info_t* info,
+ bool* file_copied) {
+ if (CopyElfFromFile(info, file_copied)) {
+ return;
+ }
+ *file_copied = false;
+
+ // Try to create the elf from memory, this will handle cases where
+ // the data only exists in memory such as vdso data on x86.
+ if (CreateElfFromMemory(process_memory, info)) {
+ return;
+ }
+
+ printf("Cannot save memory or file for map ");
+ if (!info->name.empty()) {
+ printf("%s\n", info->name.c_str());
+ } else {
+ printf("anonymous:%" PRIx64 "\n", info->start);
+ }
+}
+
+int SaveData(pid_t pid) {
+ unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+ if (regs == nullptr) {
+ printf("Unable to get remote reg data.\n");
+ return 1;
+ }
+
+ // Save the current state of the registers.
+ if (!SaveRegs(regs)) {
+ return 1;
+ }
+
+ // Do an unwind so we know how much of the stack to save, and what
+ // elf files are involved.
+ unwindstack::UnwinderFromPid unwinder(1024, pid);
+ if (!unwinder.Init(regs->Arch())) {
+ printf("Unable to init unwinder object.\n");
+ return 1;
+ }
+ unwinder.SetRegs(regs);
+ uint64_t sp = regs->sp();
+ unwinder.Unwind();
+
+ std::unordered_map<uint64_t, map_info_t> maps_by_start;
+ std::vector<std::pair<uint64_t, uint64_t>> stacks;
+ unwindstack::Maps* maps = unwinder.GetMaps();
+ uint64_t sp_map_start = 0;
+ unwindstack::MapInfo* map_info = maps->Find(sp);
+ if (map_info != nullptr) {
+ stacks.emplace_back(std::make_pair(sp, map_info->end));
+ sp_map_start = map_info->start;
+ }
+
+ for (const auto& frame : unwinder.frames()) {
+ map_info = maps->Find(frame.sp);
+ if (map_info != nullptr && sp_map_start != map_info->start) {
+ stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
+ sp_map_start = map_info->start;
+ }
+
+ if (maps_by_start.count(frame.map_start) == 0) {
+ map_info = maps->Find(frame.map_start);
+ if (map_info == nullptr) {
+ continue;
+ }
+
+ auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+ bool file_copied = false;
+ SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
+
+ // If you are using a a linker that creates two maps (one read-only, one
+ // read-executable), it's necessary to capture the previous map
+ // information if needed.
+ unwindstack::MapInfo* prev_map = map_info->prev_map;
+ if (prev_map != nullptr && map_info->offset != 0 && prev_map->offset == 0 &&
+ prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
+ maps_by_start.count(prev_map->start) == 0) {
+ info = FillInAndGetMapInfo(maps_by_start, prev_map);
+ SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ printf("%s\n", unwinder.FormatFrame(i).c_str());
+ }
+
+ if (!SaveStack(pid, stacks)) {
+ return 1;
+ }
+
+ std::vector<std::pair<uint64_t, map_info_t>> sorted_maps(maps_by_start.begin(),
+ maps_by_start.end());
+ std::sort(sorted_maps.begin(), sorted_maps.end(),
+ [](auto& a, auto& b) { return a.first < b.first; });
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
+ if (fp == nullptr) {
+ perror("Failed to create maps.txt");
+ return false;
+ }
+
+ for (auto& element : sorted_maps) {
+ char perms[5] = {"---p"};
+ map_info_t& map = element.second;
+ if (map.flags & PROT_READ) {
+ perms[0] = 'r';
+ }
+ if (map.flags & PROT_WRITE) {
+ perms[1] = 'w';
+ }
+ if (map.flags & PROT_EXEC) {
+ perms[2] = 'x';
+ }
+ fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
+ map.offset);
+ if (!map.name.empty()) {
+ fprintf(fp.get(), " %s", map.name.c_str());
+ }
+ fprintf(fp.get(), "\n");
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Usage: unwind_for_offline <PID>\n");
+ return 1;
+ }
+
+ pid_t pid = atoi(argv[1]);
+ if (!Attach(pid)) {
+ printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+ return 1;
+ }
+
+ int return_code = SaveData(pid);
+
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+
+ return return_code;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
new file mode 100644
index 000000000..7a6d8ba46
--- /dev/null
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
+ if (interface == nullptr) {
+ printf("No ARM Unwind Information.\n\n");
+ return;
+ }
+
+ printf("ARM Unwind Information:\n");
+ uint64_t load_bias = elf->GetLoadBias();
+ for (const auto& entry : interface->pt_loads()) {
+ printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
+ entry.second.offset + entry.second.table_size + load_bias);
+ for (auto pc : *interface) {
+ std::string name;
+ printf(" PC 0x%" PRIx64, pc + load_bias);
+ uint64_t func_offset;
+ if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
+ printf(" <%s>", name.c_str());
+ }
+ printf("\n");
+ uint64_t entry;
+ if (!interface->FindEntry(pc, &entry)) {
+ printf(" Cannot find entry for address.\n");
+ continue;
+ }
+ ArmExidx arm(nullptr, interface->memory(), nullptr);
+ arm.set_log(ARM_LOG_FULL);
+ arm.set_log_skip_execution(true);
+ arm.set_log_indent(2);
+ if (!arm.ExtractEntryData(entry)) {
+ if (arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to extract data.\n");
+ }
+ continue;
+ }
+ if (arm.data()->size() > 0) {
+ if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to evaluate dwarf data.\n");
+ }
+ }
+ }
+ }
+ printf("\n");
+}
+
+void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
+ for (const DwarfFde* fde : *section) {
+ // Sometimes there are entries that have empty length, skip those since
+ // they don't contain any interesting information.
+ if (fde == nullptr || fde->pc_start == fde->pc_end) {
+ continue;
+ }
+ printf("\n PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
+ std::string name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
+ printf(" <%s>", name.c_str());
+ }
+ printf("\n");
+ if (!section->Log(2, UINT64_MAX, fde)) {
+ printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
+ }
+ }
+}
+
+int GetElfInfo(const char* file, uint64_t offset) {
+ // Send all log messages to stdout.
+ log_to_stdout(true);
+
+ Elf elf(Memory::CreateFileMemory(file, offset).release());
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", file);
+ return 1;
+ }
+
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
+ printf("Soname: %s\n", soname.c_str());
+ }
+
+ std::string build_id = elf.GetBuildID();
+ if (!build_id.empty()) {
+ printf("Build ID: ");
+ for (size_t i = 0; i < build_id.size(); ++i) {
+ printf("%02hhx", build_id[i]);
+ }
+ printf("\n");
+ }
+
+ ElfInterface* interface = elf.interface();
+ if (elf.machine_type() == EM_ARM) {
+ DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
+ printf("\n");
+ }
+
+ if (interface->eh_frame() != nullptr) {
+ printf("eh_frame information:\n");
+ DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
+ printf("\n");
+ } else {
+ printf("\nno eh_frame information\n");
+ }
+
+ if (interface->debug_frame() != nullptr) {
+ printf("\ndebug_frame information:\n");
+ DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
+ printf("\n");
+ } else {
+ printf("\nno debug_frame information\n");
+ }
+
+ // If there is a gnu_debugdata interface, dump the information for that.
+ ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+ if (gnu_debugdata_interface != nullptr) {
+ if (gnu_debugdata_interface->eh_frame() != nullptr) {
+ printf("\ngnu_debugdata (eh_frame):\n");
+ DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
+ printf("\n");
+ }
+ if (gnu_debugdata_interface->debug_frame() != nullptr) {
+ printf("\ngnu_debugdata (debug_frame):\n");
+ DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
+ printf("\n");
+ }
+ } else {
+ printf("\nno valid gnu_debugdata information\n");
+ }
+
+ return 0;
+}
+
+} // namespace unwindstack
+
+int main(int argc, char** argv) {
+ if (argc != 2 && argc != 3) {
+ printf("Usage: unwind_info ELF_FILE [OFFSET]\n");
+ printf(" ELF_FILE\n");
+ printf(" The path to an elf file.\n");
+ printf(" OFFSET\n");
+ printf(" Use the offset into the ELF file as the beginning of the elf.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ uint64_t offset = 0;
+ if (argc == 3) {
+ char* end;
+ offset = strtoull(argv[2], &end, 16);
+ if (*end != '\0') {
+ printf("Malformed OFFSET value: %s\n", argv[2]);
+ return 1;
+ }
+ }
+
+ return unwindstack::GetElfInfo(argv[1], offset);
+}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
new file mode 100644
index 000000000..0cbcac5a0
--- /dev/null
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+#include "ArmExidx.h"
+#include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void PrintSignedValue(int64_t value) {
+ if (value < 0) {
+ printf("- %" PRId64, -value);
+ } else if (value > 0) {
+ printf("+ %" PRId64, value);
+ }
+}
+
+void PrintExpression(Memory* memory, uint8_t class_type, uint64_t end, uint64_t length) {
+ std::vector<std::string> lines;
+ DwarfMemory dwarf_memory(memory);
+ if (class_type == ELFCLASS32) {
+ DwarfOp<uint32_t> op(&dwarf_memory, nullptr);
+ op.GetLogInfo(end - length, end, &lines);
+ } else {
+ DwarfOp<uint64_t> op(&dwarf_memory, nullptr);
+ op.GetLogInfo(end - length, end, &lines);
+ }
+ for (auto& line : lines) {
+ printf(" %s\n", line.c_str());
+ }
+}
+
+void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
+ const DwarfFde* fde = section->GetFdeFromPc(pc);
+ if (fde == nullptr) {
+ printf(" No fde found.\n");
+ return;
+ }
+
+ dwarf_loc_regs_t regs;
+ if (!section->GetCfaLocationInfo(pc, fde, &regs)) {
+ printf(" Cannot get location information.\n");
+ return;
+ }
+
+ std::vector<std::pair<uint32_t, DwarfLocation>> loc_regs;
+ for (auto& loc : regs) {
+ loc_regs.push_back(loc);
+ }
+ std::sort(loc_regs.begin(), loc_regs.end(), [](auto a, auto b) {
+ if (a.first == CFA_REG) {
+ return true;
+ } else if (b.first == CFA_REG) {
+ return false;
+ }
+ return a.first < b.first;
+ });
+
+ for (auto& entry : loc_regs) {
+ const DwarfLocation* loc = &entry.second;
+ if (entry.first == CFA_REG) {
+ printf(" cfa = ");
+ } else {
+ printf(" r%d = ", entry.first);
+ }
+ switch (loc->type) {
+ case DWARF_LOCATION_OFFSET:
+ printf("[cfa ");
+ PrintSignedValue(loc->values[0]);
+ printf("]\n");
+ break;
+
+ case DWARF_LOCATION_VAL_OFFSET:
+ printf("cfa ");
+ PrintSignedValue(loc->values[0]);
+ printf("\n");
+ break;
+
+ case DWARF_LOCATION_REGISTER:
+ printf("r%" PRId64 " ", loc->values[0]);
+ PrintSignedValue(loc->values[1]);
+ printf("\n");
+ break;
+
+ case DWARF_LOCATION_EXPRESSION: {
+ printf("EXPRESSION\n");
+ PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+ break;
+ }
+
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ printf("VAL EXPRESSION\n");
+ PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+ break;
+ }
+
+ case DWARF_LOCATION_UNDEFINED:
+ printf("undefine\n");
+ break;
+
+ case DWARF_LOCATION_INVALID:
+ printf("INVALID\n");
+ break;
+ }
+ }
+}
+
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+ printf("\nArm exidx:\n");
+ uint64_t entry_offset;
+ if (!interface->FindEntry(pc, &entry_offset)) {
+ return;
+ }
+
+ ArmExidx arm(nullptr, interface->memory(), nullptr);
+
+ log_to_stdout(true);
+ arm.set_log(ARM_LOG_BY_REG);
+ arm.set_log_skip_execution(true);
+ arm.set_log_indent(1);
+ if (!arm.ExtractEntryData(entry_offset)) {
+ if (arm.status() != ARM_STATUS_NO_UNWIND) {
+ printf(" Error trying to extract data.\n");
+ }
+ return;
+ }
+ if (arm.data()->size() != 0 && arm.Eval()) {
+ arm.LogByReg();
+ } else {
+ printf(" Error tring to evaluate exidx data.\n");
+ }
+}
+
+int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
+ Elf elf(Memory::CreateFileMemory(file, offset).release());
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", file);
+ return 1;
+ }
+
+ ElfInterface* interface = elf.interface();
+ uint64_t load_bias = elf.GetLoadBias();
+ if (pc < load_bias) {
+ printf("PC is less than load bias.\n");
+ return 1;
+ }
+
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
+ printf("Soname: %s\n\n", soname.c_str());
+ }
+
+ printf("PC 0x%" PRIx64, pc);
+ std::string function_name;
+ uint64_t function_offset;
+ if (elf.GetFunctionName(pc, &function_name, &function_offset)) {
+ printf(" (%s)", function_name.c_str());
+ }
+ printf(":\n");
+
+ if (elf.machine_type() == EM_ARM) {
+ PrintArmRegInformation(reinterpret_cast<ElfInterfaceArm*>(interface), pc - load_bias);
+ }
+
+ DwarfSection* section = interface->eh_frame();
+ if (section != nullptr) {
+ printf("\neh_frame:\n");
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+ } else {
+ printf("\nno eh_frame information\n");
+ }
+
+ section = interface->debug_frame();
+ if (section != nullptr) {
+ printf("\ndebug_frame:\n");
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+ printf("\n");
+ } else {
+ printf("\nno debug_frame information\n");
+ }
+
+ // If there is a gnu_debugdata interface, dump the information for that.
+ ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+ if (gnu_debugdata_interface != nullptr) {
+ section = gnu_debugdata_interface->eh_frame();
+ if (section != nullptr) {
+ printf("\ngnu_debugdata (eh_frame):\n");
+ PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+ printf("\n");
+ } else {
+ printf("\nno gnu_debugdata (eh_frame)\n");
+ }
+
+ section = gnu_debugdata_interface->debug_frame();
+ if (section != nullptr) {
+ printf("\ngnu_debugdata (debug_frame):\n");
+ PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+ printf("\n");
+ } else {
+ printf("\nno gnu_debugdata (debug_frame)\n");
+ }
+ } else {
+ printf("\nno valid gnu_debugdata information\n");
+ }
+
+ return 0;
+}
+
+} // namespace unwindstack
+
+int main(int argc, char** argv) {
+ if (argc != 3 && argc != 4) {
+ printf("Usage: unwind_reg_info ELF_FILE PC [OFFSET]\n");
+ printf(" ELF_FILE\n");
+ printf(" The path to an elf file.\n");
+ printf(" PC\n");
+ printf(" The pc for which the register information should be obtained.\n");
+ printf(" OFFSET\n");
+ printf(" Use the offset into the ELF file as the beginning of the elf.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ uint64_t pc = 0;
+ char* end;
+ pc = strtoull(argv[2], &end, 16);
+ if (*end != '\0') {
+ printf("Malformed OFFSET value: %s\n", argv[2]);
+ return 1;
+ }
+
+ uint64_t offset = 0;
+ if (argc == 4) {
+ char* end;
+ offset = strtoull(argv[3], &end, 16);
+ if (*end != '\0') {
+ printf("Malformed OFFSET value: %s\n", argv[3]);
+ return 1;
+ }
+ }
+
+ return unwindstack::GetInfo(argv[1], offset, pc);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 000000000..8df228412
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+int main(int argc, char** argv) {
+ if (argc != 2 && argc != 3) {
+ printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
+ printf(" Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
+ printf(" specified, then get the function at that address.\n");
+ printf(" FUNC_ADDRESS must be a hex number.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ uint64_t func_addr;
+ if (argc == 3) {
+ char* name;
+ func_addr = strtoull(argv[2], &name, 16);
+ if (*name != '\0') {
+ printf("%s is not a hex number.\n", argv[2]);
+ return 1;
+ }
+ }
+
+ // Send all log messages to stdout.
+ unwindstack::log_to_stdout(true);
+
+ unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", argv[1]);
+ return 1;
+ }
+
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
+ printf("Soname: %s\n\n", soname.c_str());
+ }
+
+ switch (elf.machine_type()) {
+ case EM_ARM:
+ printf("ABI: arm\n");
+ break;
+ case EM_AARCH64:
+ printf("ABI: arm64\n");
+ break;
+ case EM_386:
+ printf("ABI: x86\n");
+ break;
+ case EM_X86_64:
+ printf("ABI: x86_64\n");
+ break;
+ default:
+ printf("ABI: unknown\n");
+ return 1;
+ }
+
+ std::string name;
+ if (argc == 3) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
+ printf("No known function at 0x%" PRIx64 "\n", func_addr);
+ return 1;
+ }
+ printf("<0x%" PRIx64 ">", func_addr - func_offset);
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(": %s\n", cur_name.c_str());
+ return 0;
+ }
+
+ // This is a crude way to get the symbols in order.
+ for (const auto& entry : elf.interface()->pt_loads()) {
+ uint64_t start = entry.second.offset;
+ uint64_t end = entry.second.table_size;
+ for (uint64_t addr = start; addr < end; addr += 4) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+ if (cur_name != name) {
+ printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+ }
+ name = cur_name;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 38833179b..fc6f305c0 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libusbhost",
vendor_available: true,
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 3bed0e367..415488fc0 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -597,11 +597,6 @@ struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_ite
if (iter->curr_desc >= iter->config_end)
return NULL;
next = (struct usb_descriptor_header*)iter->curr_desc;
- // Corrupt descriptor with zero length, cannot continue iterating
- if (next->bLength == 0) {
- D("usb_descriptor_iter_next got zero length USB descriptor, ending iteration\n");
- return NULL;
- }
iter->curr_desc += next->bLength;
return next;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 13e4c02de..85f428067 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -1,24 +1,21 @@
-package {
- default_applicable_licenses: ["system_core_libutils_license"],
-}
-
-license {
- name: "system_core_libutils_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
cc_library_headers {
name: "libutils_headers",
vendor_available: true,
- product_available: true,
recovery_available: true,
- vendor_ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
@@ -46,13 +43,7 @@ cc_library_headers {
header_libs: ["libbacktrace_headers"],
export_header_lib_headers: ["libbacktrace_headers"],
},
- linux_glibc: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
- },
linux_bionic: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
enabled: true,
},
windows: {
@@ -64,7 +55,6 @@ cc_library_headers {
cc_defaults {
name: "libutils_defaults",
vendor_available: true,
- product_available: true,
recovery_available: true,
vndk: {
enabled: true,
@@ -75,7 +65,6 @@ cc_defaults {
cflags: [
"-Wall",
"-Werror",
- "-Wno-exit-time-destructors",
],
header_libs: [
"libbase_headers",
@@ -89,8 +78,11 @@ cc_defaults {
"libcutils",
"liblog",
],
- sanitize: {
- misc_undefined: ["integer"],
+
+ arch: {
+ mips: {
+ cflags: ["-DALIGN_DOUBLE"],
+ },
},
target: {
@@ -141,9 +133,9 @@ cc_library {
"Errors.cpp",
"FileMap.cpp",
"JenkinsHash.cpp",
- "LightRefBase.cpp",
"NativeHandle.cpp",
"Printer.cpp",
+ "PropertyMap.cpp",
"RefBase.cpp",
"SharedBuffer.cpp",
"StopWatch.cpp",
@@ -190,9 +182,15 @@ cc_library {
"CallStack.cpp",
],
+ arch: {
+ mips: {
+ cflags: ["-DALIGN_DOUBLE"],
+ },
+ },
+
shared_libs: [
- "libutils",
- "libbacktrace",
+ "libutils",
+ "libbacktrace",
],
target: {
@@ -210,88 +208,6 @@ cc_library {
},
}
-cc_defaults {
- name: "libutils_fuzz_defaults",
- host_supported: true,
- shared_libs: [
- "libutils",
- "libbase",
- "liblog",
- ],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_bitset",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["BitSet_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_filemap",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["FileMap_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_string8",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["String8_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_string16",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["String16_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_vector",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["Vector_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_printer",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["Printer_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_callstack",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["CallStack_fuzz.cpp"],
- shared_libs: [
- "libutilscallstack",
- ],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_process_callstack",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["ProcessCallStack_fuzz.cpp"],
- shared_libs: [
- "libutilscallstack",
- ],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_refbase",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["RefBase_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_lrucache",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["LruCache_fuzz.cpp"],
-}
-
-cc_fuzz {
- name: "libutils_fuzz_looper",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["Looper_fuzz.cpp"],
-}
-
cc_test {
name: "libutils_test",
host_supported: true,
@@ -302,11 +218,9 @@ cc_test {
"LruCache_test.cpp",
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
- "Singleton_test.cpp",
"String8_test.cpp",
"String16_test.cpp",
"StrongPointer_test.cpp",
- "Timers_test.cpp",
"Unicode_test.cpp",
"Vector_test.cpp",
],
@@ -339,11 +253,6 @@ cc_test {
},
},
- data_libs: [
- "libutils_test_singleton1",
- "libutils_test_singleton2",
- ],
-
cflags: [
"-Wall",
"-Wextra",
@@ -354,10 +263,29 @@ cc_test {
test_suites: ["device-tests"],
}
+// TODO: the test infrastructure isn't yet capable of running this,
+// so it's broken out into its own test so that the main libutils_tests
+// can be in presubmit even if this can't.
+
+cc_test {
+ name: "libutils_singleton_test",
+ srcs: ["Singleton_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: ["libbase"],
+
+ required: [
+ ":libutils_test_singleton1",
+ ":libutils_test_singleton2",
+ ],
+}
+
cc_test_library {
name: "libutils_test_singleton1",
host_supported: true,
- installable: false,
+ relative_install_path: "libutils_test",
srcs: ["Singleton_test1.cpp"],
cflags: [
"-Wall",
@@ -368,7 +296,7 @@ cc_test_library {
cc_test_library {
name: "libutils_test_singleton2",
host_supported: true,
- installable: false,
+ relative_install_path: "libutils_test",
srcs: ["Singleton_test2.cpp"],
cflags: [
"-Wall",
diff --git a/libutils/BitSet_fuzz.cpp b/libutils/BitSet_fuzz.cpp
deleted file mode 100644
index 2e6043cf0..000000000
--- a/libutils/BitSet_fuzz.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <functional>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/BitSet.h"
-static constexpr uint8_t MAX_OPERATIONS = 50;
-
-// We need to handle both 32 and 64 bit bitsets, so we use a function template
-// here. Sadly, std::function can't be generic, so we generate a vector of
-// std::functions using this function.
-template <typename T>
-std::vector<std::function<void(T, uint32_t)>> getOperationsForType() {
- return {
- [](T bs, uint32_t val) -> void { bs.markBit(val); },
- [](T bs, uint32_t val) -> void { bs.valueForBit(val); },
- [](T bs, uint32_t val) -> void { bs.hasBit(val); },
- [](T bs, uint32_t val) -> void { bs.clearBit(val); },
- [](T bs, uint32_t val) -> void { bs.getIndexOfBit(val); },
- [](T bs, uint32_t) -> void { bs.clearFirstMarkedBit(); },
- [](T bs, uint32_t) -> void { bs.markFirstUnmarkedBit(); },
- [](T bs, uint32_t) -> void { bs.clearLastMarkedBit(); },
- [](T bs, uint32_t) -> void { bs.clear(); },
- [](T bs, uint32_t) -> void { bs.count(); },
- [](T bs, uint32_t) -> void { bs.isEmpty(); },
- [](T bs, uint32_t) -> void { bs.isFull(); },
- [](T bs, uint32_t) -> void { bs.firstMarkedBit(); },
- [](T bs, uint32_t) -> void { bs.lastMarkedBit(); },
- };
-}
-
-// Our operations for 32 and 64 bit bitsets
-static const std::vector<std::function<void(android::BitSet32, uint32_t)>> thirtyTwoBitOps =
- getOperationsForType<android::BitSet32>();
-static const std::vector<std::function<void(android::BitSet64, uint32_t)>> sixtyFourBitOps =
- getOperationsForType<android::BitSet64>();
-
-void runOperationFor32Bit(android::BitSet32 bs, uint32_t bit, uint8_t operation) {
- thirtyTwoBitOps[operation](bs, bit);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- uint32_t thirty_two_base = dataProvider.ConsumeIntegral<uint32_t>();
- uint64_t sixty_four_base = dataProvider.ConsumeIntegral<uint64_t>();
- android::BitSet32 b1 = android::BitSet32(thirty_two_base);
- android::BitSet64 b2 = android::BitSet64(sixty_four_base);
-
- size_t opsRun = 0;
- while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
- uint32_t bit = dataProvider.ConsumeIntegral<uint32_t>();
- uint8_t op = dataProvider.ConsumeIntegral<uint8_t>();
- thirtyTwoBitOps[op % thirtyTwoBitOps.size()](b1, bit);
- sixtyFourBitOps[op % sixtyFourBitOps.size()](b2, bit);
- }
- return 0;
-}
diff --git a/libutils/CallStack_fuzz.cpp b/libutils/CallStack_fuzz.cpp
deleted file mode 100644
index e89b5b7ca..000000000
--- a/libutils/CallStack_fuzz.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory.h>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/CallStack.h"
-
-static constexpr int MAX_STRING_SIZE = 500;
-static constexpr int MAX_IGNORE_DEPTH = 200;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- size_t ignoreDepth = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_IGNORE_DEPTH);
- int logPriority = dataProvider.ConsumeIntegral<int>();
- pid_t tid = dataProvider.ConsumeIntegral<pid_t>();
- std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
- std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
-
- const char* logTagChars = logTag.c_str();
- const char* prefixChars = prefix.c_str();
-
- android::CallStack::CallStackUPtr callStack = android::CallStack::getCurrent(ignoreDepth);
- android::CallStack* callstackPtr = callStack.get();
- android::CallStack::logStack(logTagChars, callstackPtr,
- static_cast<android_LogPriority>(logPriority));
- android::CallStack::stackToString(prefixChars);
-
- callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
- callstackPtr->clear();
- callstackPtr->getCurrent(ignoreDepth);
- callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
- callstackPtr->update(ignoreDepth, tid);
- callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
-
- return 0;
-}
diff --git a/libutils/FileMap_fuzz.cpp b/libutils/FileMap_fuzz.cpp
deleted file mode 100644
index d800564f1..000000000
--- a/libutils/FileMap_fuzz.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <iostream>
-
-#include "android-base/file.h"
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/FileMap.h"
-
-static constexpr uint16_t MAX_STR_SIZE = 256;
-static constexpr uint8_t MAX_FILENAME_SIZE = 32;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- TemporaryFile tf;
- // Generate file contents
- std::string contents = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
- // If we have string contents, dump them into the file.
- // Otherwise, just leave it as an empty file.
- if (contents.length() > 0) {
- const char* bytes = contents.c_str();
- android::base::WriteStringToFd(bytes, tf.fd);
- }
- android::FileMap m;
- // Generate create() params
- std::string orig_name = dataProvider.ConsumeRandomLengthString(MAX_FILENAME_SIZE);
- size_t length = dataProvider.ConsumeIntegralInRange<size_t>(1, SIZE_MAX);
- off64_t offset = dataProvider.ConsumeIntegralInRange<off64_t>(1, INT64_MAX);
- bool read_only = dataProvider.ConsumeBool();
- m.create(orig_name.c_str(), tf.fd, offset, length, read_only);
- m.getDataOffset();
- m.getFileName();
- m.getDataLength();
- m.getDataPtr();
- int enum_index = dataProvider.ConsumeIntegral<int>();
- m.advise(static_cast<android::FileMap::MapAdvice>(enum_index));
- return 0;
-}
diff --git a/libutils/FuzzFormatTypes.h b/libutils/FuzzFormatTypes.h
deleted file mode 100644
index aa9e503ff..000000000
--- a/libutils/FuzzFormatTypes.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <string>
-
-static const std::string kFormatChars = std::string("duoxXfFeEgGaAcsp");
-static constexpr int32_t kMaxFormatFlagValue = INT16_MAX;
-enum FormatChar : uint8_t {
- SIGNED_DECIMAL = 0,
- UNSIGNED_DECIMAL = 1,
- UNSIGNED_OCTAL = 2,
- UNSIGNED_HEX_LOWER = 3,
- UNSIGNED_HEX_UPPER = 4,
- // Uppercase/lowercase floating point impacts 'inf', 'infinity', and 'nan'
- FLOAT_LOWER = 5,
- FLOAT_UPPER = 6,
- // Upper/lower impacts the "e" in exponents.
- EXPONENT_LOWER = 7,
- EXPONENT_UPPER = 8,
- // %g will use %e or %f, whichever is shortest
- SHORT_EXP_LOWER = 9,
- // %G will use %E or %F, whichever is shortest
- SHORT_EXP_UPPER = 10,
- HEX_FLOAT_LOWER = 11,
- HEX_FLOAT_UPPER = 12,
- CHAR = 13,
- STRING = 14,
- POINTER = 15,
- // Used by libfuzzer
- kMaxValue = POINTER
-};
-
-bool canApplyFlag(FormatChar formatChar, char modifier) {
- if (modifier == '#') {
- return formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
- formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
- formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
- formatChar == SHORT_EXP_UPPER;
- } else if (modifier == '.') {
- return formatChar == SIGNED_DECIMAL || formatChar == UNSIGNED_DECIMAL ||
- formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||
- formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||
- formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||
- formatChar == SHORT_EXP_UPPER || formatChar == STRING;
- }
- return true;
-}
diff --git a/libutils/LightRefBase.cpp b/libutils/LightRefBase.cpp
deleted file mode 100644
index e08ffecfc..000000000
--- a/libutils/LightRefBase.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "LightRefBase"
-
-#include <utils/LightRefBase.h>
-
-#include <log/log.h>
-
-namespace android {
-
-void LightRefBase_reportIncStrongRequireStrongFailed(const void* thiz) {
- LOG_ALWAYS_FATAL("incStrongRequireStrong() called on %p which isn't already owned", thiz);
-}
-
-} // namespace android
diff --git a/libutils/Looper_fuzz.cpp b/libutils/Looper_fuzz.cpp
deleted file mode 100644
index c3ae54ece..000000000
--- a/libutils/Looper_fuzz.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/select.h>
-
-#include <iostream>
-
-#include <utils/Looper.h>
-
-#include "Looper_test_pipe.h"
-#include "fuzzer/FuzzedDataProvider.h"
-
-using android::Looper;
-using android::sp;
-
-// We don't want this to bog down fuzzing
-static constexpr int MAX_POLL_DELAY = 50;
-static constexpr int MAX_OPERATIONS = 500;
-
-void doNothing() {}
-void* doNothingPointer = reinterpret_cast<void*>(doNothing);
-
-static int noopCallback(int, int, void*) {
- return 0;
-}
-
-std::vector<std::function<void(FuzzedDataProvider*, sp<Looper>, Pipe)>> operations = {
- [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
- looper->pollOnce(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
- },
- [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
- looper->pollAll(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
- },
- // events and callback are nullptr
- [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
- looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
- dataProvider->ConsumeIntegral<int>(), nullptr, nullptr);
- },
- // Events is nullptr
- [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
- looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
- dataProvider->ConsumeIntegral<int>(), noopCallback, nullptr);
- },
- // callback is nullptr
- [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
- looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
- dataProvider->ConsumeIntegral<int>(), nullptr, doNothingPointer);
- },
- // callback and events both set
- [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
- looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
- dataProvider->ConsumeIntegral<int>(), noopCallback, doNothingPointer);
- },
-
- [](FuzzedDataProvider*, sp<Looper> looper, Pipe) -> void { looper->wake(); },
- [](FuzzedDataProvider*, sp<Looper>, Pipe pipeObj) -> void { pipeObj.writeSignal(); }};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- Pipe pipeObj;
- FuzzedDataProvider dataProvider(data, size);
- sp<Looper> looper = new Looper(dataProvider.ConsumeBool());
-
- size_t opsRun = 0;
- while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
- uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- operations[op](&dataProvider, looper, pipeObj);
- }
- // Clear our pointer
- looper.clear();
- return 0;
-}
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 34f424b83..37bdf0544 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -2,13 +2,12 @@
// Copyright 2010 The Android Open Source Project
//
-#include <gtest/gtest.h>
-#include <time.h>
-#include <unistd.h>
#include <utils/Looper.h>
-#include <utils/StopWatch.h>
#include <utils/Timers.h>
-#include "Looper_test_pipe.h"
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
#include <utils/threads.h>
@@ -25,6 +24,41 @@ enum {
MSG_TEST4 = 4,
};
+class Pipe {
+public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
+
class DelayedTask : public Thread {
int mDelayMillis;
diff --git a/libutils/Looper_test_pipe.h b/libutils/Looper_test_pipe.h
deleted file mode 100644
index 77b7b8b36..000000000
--- a/libutils/Looper_test_pipe.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <unistd.h>
-/**
- * A pipe class for use when testing or fuzzing Looper
- */
-class Pipe {
- public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- android::status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- android::status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
diff --git a/libutils/LruCache_fuzz.cpp b/libutils/LruCache_fuzz.cpp
deleted file mode 100644
index f8bacfcbc..000000000
--- a/libutils/LruCache_fuzz.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <functional>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/LruCache.h"
-#include "utils/StrongPointer.h"
-
-typedef android::LruCache<size_t, size_t> FuzzCache;
-
-static constexpr uint32_t MAX_CACHE_ENTRIES = 800;
-
-class NoopRemovedCallback : public android::OnEntryRemoved<size_t, size_t> {
- public:
- void operator()(size_t&, size_t&) {
- // noop
- }
-};
-
-static NoopRemovedCallback callback;
-
-static const std::vector<std::function<void(FuzzedDataProvider*, FuzzCache*)>> operations = {
- [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->removeOldest(); },
- [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->peekOldestValue(); },
- [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->clear(); },
- [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->size(); },
- [](FuzzedDataProvider*, FuzzCache* cache) -> void {
- android::LruCache<size_t, size_t>::Iterator iter(*cache);
- while (iter.next()) {
- iter.key();
- iter.value();
- }
- },
- [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
- size_t key = dataProvider->ConsumeIntegral<size_t>();
- size_t val = dataProvider->ConsumeIntegral<size_t>();
- cache->put(key, val);
- },
- [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
- size_t key = dataProvider->ConsumeIntegral<size_t>();
- cache->get(key);
- },
- [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
- size_t key = dataProvider->ConsumeIntegral<size_t>();
- cache->remove(key);
- },
- [](FuzzedDataProvider*, FuzzCache* cache) -> void {
- cache->setOnEntryRemovedListener(&callback);
- }};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- FuzzCache cache(MAX_CACHE_ENTRIES);
- while (dataProvider.remaining_bytes() > 0) {
- uint8_t op = dataProvider.ConsumeIntegral<uint8_t>() % operations.size();
- operations[op](&dataProvider, &cache);
- }
-
- return 0;
-}
diff --git a/libutils/Printer_fuzz.cpp b/libutils/Printer_fuzz.cpp
deleted file mode 100755
index 0180d41f6..000000000
--- a/libutils/Printer_fuzz.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android-base/file.h"
-#include "android/log.h"
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Printer.h"
-#include "utils/String8.h"
-static constexpr int MAX_STR_SIZE = 1000;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- android::String8 outStr = android::String8();
- // Line indent/formatting
- uint indent = dataProvider.ConsumeIntegral<uint>();
- std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
- std::string line = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
-
- // Misc properties
- std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
- android_LogPriority priority =
- static_cast<android_LogPriority>(dataProvider.ConsumeIntegral<int>());
- bool ignoreBlankLines = dataProvider.ConsumeBool();
-
- TemporaryFile tf;
- android::FdPrinter filePrinter = android::FdPrinter(tf.fd, indent, prefix.c_str());
- android::String8Printer stringPrinter = android::String8Printer(&outStr);
- android::PrefixPrinter printer = android::PrefixPrinter(stringPrinter, prefix.c_str());
- android::LogPrinter logPrinter =
- android::LogPrinter(logTag.c_str(), priority, prefix.c_str(), ignoreBlankLines);
-
- printer.printLine(line.c_str());
- printer.printFormatLine("%s", line.c_str());
- logPrinter.printLine(line.c_str());
- logPrinter.printFormatLine("%s", line.c_str());
- filePrinter.printLine(line.c_str());
- filePrinter.printFormatLine("%s", line.c_str());
- return 0;
-}
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
deleted file mode 100644
index 30136cda7..000000000
--- a/libutils/ProcessCallStack_fuzz.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <atomic>
-#include <thread>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/ProcessCallStack.h"
-using android::ProcessCallStack;
-
-static constexpr int MAX_NAME_SIZE = 1000;
-static constexpr int MAX_LOG_META_SIZE = 1000;
-static constexpr uint8_t MAX_THREADS = 10;
-
-std::atomic_bool ranCallStackUpdate(false);
-void loop() {
- while (!ranCallStackUpdate.load()) {
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- }
-}
-
-void spawnThreads(FuzzedDataProvider* dataProvider) {
- std::vector<std::thread> threads = std::vector<std::thread>();
-
- // Get the number of threads to generate
- uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
-
- // Generate threads
- for (uint8_t i = 0; i < count; i++) {
- std::string threadName =
- dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
- std::thread th = std::thread(loop);
- pthread_setname_np(th.native_handle(), threadName.c_str());
- threads.push_back(move(th));
- }
-
- // Collect thread information
- ProcessCallStack callStack = ProcessCallStack();
- callStack.update();
-
- // Tell our patiently waiting threads they can be done now.
- ranCallStackUpdate.store(true);
-
- std::string logTag = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
- std::string prefix = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
- // Both of these, along with dump, all call print() under the hood,
- // Which is covered by the Printer fuzzer.
- callStack.log(logTag.c_str());
- callStack.toString(prefix.c_str());
-
- // Check size
- callStack.size();
-
- // wait for any remaining threads
- for (auto& thread : threads) {
- thread.join();
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- spawnThreads(&dataProvider);
- return 0;
-}
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
new file mode 100644
index 000000000..f00272a0f
--- /dev/null
+++ b/libutils/PropertyMap.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PropertyMap"
+
+#include <utils/PropertyMap.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {
+}
+
+PropertyMap::~PropertyMap() {
+}
+
+void PropertyMap::clear() {
+ mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+ return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+ int32_t intValue;
+ if (!tryGetProperty(key, intValue)) {
+ return false;
+ }
+
+ outValue = intValue;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), & end, 10);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), & end);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a float.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+void PropertyMap::addAll(const PropertyMap* map) {
+ for (size_t i = 0; i < map->mProperties.size(); i++) {
+ mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+ }
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+ *outMap = nullptr;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening property file %s.", status, filename.string());
+ } else {
+ PropertyMap* map = new PropertyMap();
+ if (!map) {
+ ALOGE("Error allocating property map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
+ }
+ return status;
+}
+
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) :
+ mMap(map), mTokenizer(tokenizer) {
+}
+
+PropertyMap::Parser::~Parser() {
+}
+
+status_t PropertyMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ if (keyToken.isEmpty()) {
+ ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (mTokenizer->nextChar() != '=') {
+ ALOGE("%s: Expected '=' between property key and value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+ if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+ ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ ALOGE("%s: Expected end of line, got '%s'.",
+ mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+
+ if (mMap->hasProperty(keyToken)) {
+ ALOGE("%s: Duplicate property value for key '%s'.",
+ mTokenizer->getLocation().string(), keyToken.string());
+ return BAD_VALUE;
+ }
+
+ mMap->addProperty(keyToken, valueToken);
+ }
+
+ mTokenizer->nextLine();
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/libutils/README b/libutils/README
new file mode 100644
index 000000000..01741e093
--- /dev/null
+++ b/libutils/README
@@ -0,0 +1,289 @@
+Android Utility Function Library
+================================
+
+
+If you need a feature that is native to Linux but not present on other
+platforms, construct a platform-dependent implementation that shares
+the Linux interface. That way the actual device runs as "light" as
+possible.
+
+If that isn't feasible, create a system-independent interface and hide
+the details.
+
+The ultimate goal is *not* to create a super-duper platform abstraction
+layer. The goal is to provide an optimized solution for Linux with
+reasonable implementations for other platforms.
+
+
+
+Resource overlay
+================
+
+
+Introduction
+------------
+
+Overlay packages are special .apk files which provide no code but
+additional resource values (and possibly new configurations) for
+resources in other packages. When an application requests resources,
+the system will return values from either the application's original
+package or any associated overlay package. Any redirection is completely
+transparent to the calling application.
+
+Resource values have the following precedence table, listed in
+descending precedence.
+
+ * overlay package, matching config (eg res/values-en-land)
+
+ * original package, matching config
+
+ * overlay package, no config (eg res/values)
+
+ * original package, no config
+
+During compilation, overlay packages are differentiated from regular
+packages by passing the -o flag to aapt.
+
+
+Background
+----------
+
+This section provides generic background material on resources in
+Android.
+
+
+How resources are bundled in .apk files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Android .apk files are .zip files, usually housing .dex code,
+certificates and resources, though packages containing resources but
+no code are possible. Resources can be divided into the following
+categories; a `configuration' indicates a set of phone language, display
+density, network operator, etc.
+
+ * assets: uncompressed, raw files packaged as part of an .apk and
+ explicitly referenced by filename. These files are
+ independent of configuration.
+
+ * res/drawable: bitmap or xml graphics. Each file may have different
+ values depending on configuration.
+
+ * res/values: integers, strings, etc. Each resource may have different
+ values depending on configuration.
+
+Resource meta information and information proper is stored in a binary
+format in a named file resources.arsc, bundled as part of the .apk.
+
+Resource IDs and lookup
+~~~~~~~~~~~~~~~~~~~~~~~
+During compilation, the aapt tool gathers application resources and
+generates a resources.arsc file. Each resource name is assigned an
+integer ID 0xppttiii (translated to a symbolic name via R.java), where
+
+ * pp: corresponds to the package namespace (details below).
+
+ * tt: corresponds to the resource type (string, int, etc). Every
+ resource of the same type within the same package has the same
+ tt value, but depending on available types, the actual numerical
+ value may be different between packages.
+
+ * iiii: sequential number, assigned in the order resources are found.
+
+Resource values are specified paired with a set of configuration
+constraints (the default being the empty set), eg res/values-sv-port
+which imposes restrictions on language (Swedish) and display orientation
+(portrait). During lookup, every constraint set is matched against the
+current configuration, and the value corresponding to the best matching
+constraint set is returned (ResourceTypes.{h,cpp}).
+
+Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility
+is governed by AssetManager.cpp, which tracks loaded resources per
+process.
+
+Assets are looked up by path and filename in AssetManager.cpp. The path
+to resources in res/drawable are located by ResourceTypes.cpp and then
+handled like assets by AssetManager.cpp. Other resources are handled
+solely by ResourceTypes.cpp.
+
+Package ID as namespace
+~~~~~~~~~~~~~~~~~~~~~~~
+The pp part of a resource ID defines a namespace. Android currently
+defines two namespaces:
+
+ * 0x01: system resources (pre-installed in framework-res.apk)
+
+ * 0x7f: application resources (bundled in the application .apk)
+
+ResourceTypes.cpp supports package IDs between 0x01 and 0x7f
+(inclusive); values outside this range are invalid.
+
+Each running (Dalvik) process is assigned a unique instance of
+AssetManager, which in turn keeps a forest structure of loaded
+resource.arsc files. Normally, this forest is structured as follows,
+where mPackageMap is the internal vector employed in ResourceTypes.cpp.
+
+mPackageMap[0x00] -> system package
+mPackageMap[0x01] -> NULL
+mPackageMap[0x02] -> NULL
+...
+mPackageMap[0x7f - 2] -> NULL
+mPackageMap[0x7f - 1] -> application package
+
+
+
+The resource overlay extension
+------------------------------
+
+The resource overlay mechanism aims to (partly) shadow and extend
+existing resources with new values for defined and new configurations.
+Technically, this is achieved by adding resource-only packages (called
+overlay packages) to existing resource namespaces, like so:
+
+mPackageMap[0x00] -> system package -> system overlay package
+mPackageMap[0x01] -> NULL
+mPackageMap[0x02] -> NULL
+...
+mPackageMap[0x7f - 2] -> NULL
+mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2
+
+The use of overlay resources is completely transparent to
+applications; no additional resource identifiers are introduced, only
+configuration/value pairs. Any number of overlay packages may be loaded
+at a time; overlay packages are agnostic to what they target -- both
+system and application resources are fair game.
+
+The package targeted by an overlay package is called the target or
+original package.
+
+Resource overlay operates on symbolic resources names. Hence, to
+override the string/str1 resources in a package, the overlay package
+would include a resource also named string/str1. The end user does not
+have to worry about the numeric resources IDs assigned by aapt, as this
+is resolved automatically by the system.
+
+As of this writing, the use of resource overlay has not been fully
+explored. Until it has, only OEMs are trusted to use resource overlay.
+For this reason, overlay packages must reside in /system/overlay.
+
+
+Resource ID mapping
+~~~~~~~~~~~~~~~~~~~
+Resource identifiers must be coherent within the same namespace (ie
+PackageGroup in ResourceTypes.cpp). Calling applications will refer to
+resources using the IDs defined in the original package, but there is no
+guarantee aapt has assigned the same ID to the corresponding resource in
+an overlay package. To translate between the two, a resource ID mapping
+{original ID -> overlay ID} is created during package installation
+(PackageManagerService.java) and used during resource lookup. The
+mapping is stored in /data/resource-cache, with a @idmap file name
+suffix.
+
+The idmap file format is documented in a separate section, below.
+
+
+Package management
+~~~~~~~~~~~~~~~~~~
+Packages are managed by the PackageManagerService. Addition and removal
+of packages are monitored via the inotify framework, exposed via
+android.os.FileObserver.
+
+During initialization of a Dalvik process, ActivityThread.java requests
+the process' AssetManager (by proxy, via AssetManager.java and JNI)
+to load a list of packages. This list includes overlay packages, if
+present.
+
+When a target package or a corresponding overlay package is installed,
+the target package's process is stopped and a new idmap is generated.
+This is similar to how applications are stopped when their packages are
+upgraded.
+
+
+Creating overlay packages
+-------------------------
+
+Overlay packages should contain no code, define (some) resources with
+the same type and name as in the original package, and be compiled with
+the -o flag passed to aapt.
+
+The aapt -o flag instructs aapt to create an overlay package.
+Technically, this means the package will be assigned package id 0x00.
+
+There are no restrictions on overlay packages names, though the naming
+convention <original.package.name>.overlay.<name> is recommended.
+
+
+Example overlay package
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To overlay the resource bool/b in package com.foo.bar, to be applied
+when the display is in landscape mode, create a new package with
+no source code and a single .xml file under res/values-land, with
+an entry for bool/b. Compile with aapt -o and place the results in
+/system/overlay by adding the following to Android.mk:
+
+LOCAL_AAPT_FLAGS := -o com.foo.bar
+LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay
+
+
+The ID map (idmap) file format
+------------------------------
+
+The idmap format is designed for lookup performance. However, leading
+and trailing undefined overlay values are discarded to reduce the memory
+footprint.
+
+
+idmap grammar
+~~~~~~~~~~~~~
+All atoms (names in square brackets) are uint32_t integers. The
+idmap-magic constant spells "idmp" in ASCII. Offsets are given relative
+to the data_header, not to the beginning of the file.
+
+map := header data
+header := idmap-magic <crc32-original-pkg> <crc32-overlay-pkg>
+idmap-magic := <0x706d6469>
+data := data_header type_block+
+data_header := <m> header_block{m}
+header_block := <0> | <type_block_offset>
+type_block := <n> <id_offset> entry{n}
+entry := <resource_id_in_target_package>
+
+
+idmap example
+~~~~~~~~~~~~~
+Given a pair of target and overlay packages with CRC sums 0x216a8fe2
+and 0x6b9beaec, each defining the following resources
+
+Name Target package Overlay package
+string/str0 0x7f010000 -
+string/str1 0x7f010001 0x7f010000
+string/str2 0x7f010002 -
+string/str3 0x7f010003 0x7f010001
+string/str4 0x7f010004 -
+bool/bool0 0x7f020000 -
+integer/int0 0x7f030000 0x7f020000
+integer/int1 0x7f030001 -
+
+the corresponding resource map is
+
+0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \
+0x00000004 0x00000000 0x00000009 0x00000003 \
+0x00000001 0x7f010000 0x00000000 0x7f010001 \
+0x00000001 0x00000000 0x7f020000
+
+or, formatted differently
+
+0x706d6469 # magic: all idmap files begin with this constant
+0x216a8fe2 # CRC32 of the resources.arsc file in the original package
+0x6b9beaec # CRC32 of the resources.arsc file in the overlay package
+0x00000003 # header; three types (string, bool, integer) in the target package
+0x00000004 # header_block for type 0 (string) is located at offset 4
+0x00000000 # no bool type exists in overlay package -> no header_block
+0x00000009 # header_block for type 2 (integer) is located at offset 9
+0x00000003 # header_block for string; overlay IDs span 3 elements
+0x00000001 # the first string in target package is entry 1 == offset
+0x7f010000 # target 0x7f01001 -> overlay 0x7f010000
+0x00000000 # str2 not defined in overlay package
+0x7f010001 # target 0x7f010003 -> overlay 0x7f010001
+0x00000001 # header_block for integer; overlay IDs span 1 element
+0x00000000 # offset == 0
+0x7f020000 # target 0x7f030000 -> overlay 0x7f020000
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index b57e28741..ae107893c 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -21,10 +21,10 @@
#include <android-base/macros.h>
-#include <log/log.h>
-
#include <utils/RefBase.h>
+#include <utils/CallStack.h>
+
#include <utils/Mutex.h>
#ifndef __unused
@@ -55,17 +55,6 @@
// case.
#define DEBUG_REFBASE_DESTRUCTION 1
-#if !defined(_WIN32) && !defined(__APPLE__)
-// CallStack is only supported on linux type platforms.
-#define CALLSTACK_ENABLED 1
-#else
-#define CALLSTACK_ENABLED 0
-#endif
-
-#if CALLSTACK_ENABLED
-#include <utils/CallStack.h>
-#endif
-
// ---------------------------------------------------------------------------
namespace android {
@@ -196,7 +185,7 @@ public:
, mRetain(false)
{
}
-
+
~weakref_impl()
{
bool dumpStack = false;
@@ -207,7 +196,7 @@ public:
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
@@ -221,7 +210,7 @@ public:
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
@@ -229,9 +218,7 @@ public:
}
if (dumpStack) {
ALOGE("above errors at:");
-#if CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
-#endif
}
}
@@ -274,7 +261,8 @@ public:
renameRefsId(mWeakRefs, old_id, new_id);
}
- void trackMe(bool track, bool retain) {
+ void trackMe(bool track, bool retain)
+ {
mTrackEnabled = track;
mRetain = retain;
}
@@ -318,7 +306,7 @@ private:
{
ref_entry* next;
const void* id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack::CallStackUPtr stack;
#endif
int32_t ref;
@@ -335,7 +323,7 @@ private:
// decrement the reference count.
ref->ref = mRef;
ref->id = id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED
ref->stack = CallStack::getCurrent(2);
#endif
ref->next = *refs;
@@ -347,7 +335,7 @@ private:
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
-
+
ref_entry* const head = *refs;
ref_entry* ref = head;
while (ref != NULL) {
@@ -371,9 +359,7 @@ private:
ref = ref->next;
}
-#if CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
-#endif
}
}
@@ -399,7 +385,7 @@ private:
snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
inc, refs->id, refs->ref);
out->append(buf);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(CallStack::stackToString("\t\t", refs->stack.get()));
#else
out->append("\t\t(call stacks disabled)");
@@ -426,7 +412,7 @@ void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id);
-
+
refs->addStrongRef(id);
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
@@ -443,20 +429,6 @@ void RefBase::incStrong(const void* id) const
refs->mBase->onFirstRef();
}
-void RefBase::incStrongRequireStrong(const void* id) const {
- weakref_impl* const refs = mRefs;
- refs->incWeak(id);
-
- refs->addStrongRef(id);
- const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
-
- LOG_ALWAYS_FATAL_IF(c <= 0 || c == INITIAL_STRONG_VALUE,
- "incStrongRequireStrong() called on %p which isn't already owned", refs);
-#if PRINT_REFS
- ALOGD("incStrong (requiring strong) of %p from %p: cnt=%d\n", this, id, c);
-#endif
-}
-
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
@@ -496,7 +468,7 @@ void RefBase::forceIncStrong(const void* id) const
// TODO: Better document assumptions.
weakref_impl* const refs = mRefs;
refs->incWeak(id);
-
+
refs->addStrongRef(id);
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow",
@@ -535,14 +507,6 @@ void RefBase::weakref_type::incWeak(const void* id)
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
-void RefBase::weakref_type::incWeakRequireWeak(const void* id)
-{
- weakref_impl* const impl = static_cast<weakref_impl*>(this);
- impl->addWeakRef(id);
- const int32_t c __unused = impl->mWeak.fetch_add(1,
- std::memory_order_relaxed);
- LOG_ALWAYS_FATAL_IF(c <= 0, "incWeakRequireWeak called on %p which has no weak refs", this);
-}
void RefBase::weakref_type::decWeak(const void* id)
{
@@ -586,7 +550,7 @@ void RefBase::weakref_type::decWeak(const void* id)
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id);
-
+
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);
@@ -603,7 +567,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id)
// the strong count has changed on us, we need to re-assert our
// situation. curCount was updated by compare_exchange_weak.
}
-
+
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
// we're now in the harder case of either:
// - there never was a strong reference on us
@@ -660,7 +624,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id)
}
}
}
-
+
impl->addStrongRef(id);
#if PRINT_REFS
@@ -755,10 +719,7 @@ RefBase::~RefBase()
// Treating this as fatal is prone to causing boot loops. For debugging, it's
// better to treat as non-fatal.
ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
-
-#if CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
-#endif
#else
LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
#endif
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
deleted file mode 100644
index 69288b354..000000000
--- a/libutils/RefBase_fuzz.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RefBaseFuzz"
-
-#include <thread>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Log.h"
-#include "utils/RWLock.h"
-#include "utils/RefBase.h"
-#include "utils/StrongPointer.h"
-
-using android::RefBase;
-using android::RWLock;
-using android::sp;
-using android::wp;
-
-static constexpr int kMaxOperations = 100;
-static constexpr int kMaxThreads = 10;
-struct RefBaseSubclass : public RefBase {
- public:
- RefBaseSubclass(bool* deletedCheck, RWLock& deletedMtx)
- : mDeleted(deletedCheck), mRwLock(deletedMtx) {
- RWLock::AutoWLock lock(mRwLock);
- *mDeleted = false;
- extendObjectLifetime(OBJECT_LIFETIME_WEAK);
- }
-
- virtual ~RefBaseSubclass() {
- RWLock::AutoWLock lock(mRwLock);
- *mDeleted = true;
- }
-
- private:
- bool* mDeleted;
- android::RWLock& mRwLock;
-};
-
-// A thread-specific state object for ref
-struct RefThreadState {
- size_t strongCount = 0;
- size_t weakCount = 0;
-};
-
-RWLock gRefDeletedLock;
-bool gRefDeleted = false;
-bool gHasModifiedRefs = false;
-RefBaseSubclass* ref;
-RefBase::weakref_type* weakRefs;
-
-// These operations don't need locks as they explicitly check per-thread counts before running
-// they also have the potential to write to gRefDeleted, so must not be locked.
-const std::vector<std::function<void(RefThreadState*)>> kUnlockedOperations = {
- [](RefThreadState* refState) -> void {
- if (refState->strongCount > 0) {
- ref->decStrong(nullptr);
- gHasModifiedRefs = true;
- refState->strongCount--;
- }
- },
- [](RefThreadState* refState) -> void {
- if (refState->weakCount > 0) {
- weakRefs->decWeak(nullptr);
- gHasModifiedRefs = true;
- refState->weakCount--;
- }
- },
-};
-
-const std::vector<std::function<void(RefThreadState*)>> kMaybeLockedOperations = {
- // Read-only operations
- [](RefThreadState*) -> void { ref->getStrongCount(); },
- [](RefThreadState*) -> void { weakRefs->getWeakCount(); },
- [](RefThreadState*) -> void { ref->printRefs(); },
-
- // Read/write operations
- [](RefThreadState* refState) -> void {
- ref->incStrong(nullptr);
- gHasModifiedRefs = true;
- refState->strongCount++;
- },
- [](RefThreadState* refState) -> void {
- ref->forceIncStrong(nullptr);
- gHasModifiedRefs = true;
- refState->strongCount++;
- },
- [](RefThreadState* refState) -> void {
- ref->createWeak(nullptr);
- gHasModifiedRefs = true;
- refState->weakCount++;
- },
- [](RefThreadState* refState) -> void {
- // This will increment weak internally, then attempt to
- // promote it to strong. If it fails, it decrements weak.
- // If it succeeds, the weak is converted to strong.
- // Both cases net no weak reference change.
- if (weakRefs->attemptIncStrong(nullptr)) {
- refState->strongCount++;
- gHasModifiedRefs = true;
- }
- },
- [](RefThreadState* refState) -> void {
- if (weakRefs->attemptIncWeak(nullptr)) {
- refState->weakCount++;
- gHasModifiedRefs = true;
- }
- },
- [](RefThreadState* refState) -> void {
- weakRefs->incWeak(nullptr);
- gHasModifiedRefs = true;
- refState->weakCount++;
- },
-};
-
-void loop(const std::vector<uint8_t>& fuzzOps) {
- RefThreadState state;
- uint8_t lockedOpSize = kMaybeLockedOperations.size();
- uint8_t totalOperationTypes = lockedOpSize + kUnlockedOperations.size();
- for (auto op : fuzzOps) {
- auto opVal = op % totalOperationTypes;
- if (opVal >= lockedOpSize) {
- kUnlockedOperations[opVal % lockedOpSize](&state);
- } else {
- // We only need to lock if we have no strong or weak count
- bool shouldLock = state.strongCount == 0 && state.weakCount == 0;
- if (shouldLock) {
- gRefDeletedLock.readLock();
- // If ref has deleted itself, we can no longer fuzz on this thread.
- if (gRefDeleted) {
- // Unlock since we're exiting the loop here.
- gRefDeletedLock.unlock();
- return;
- }
- }
- // Execute the locked operation
- kMaybeLockedOperations[opVal](&state);
- // Unlock if we locked.
- if (shouldLock) {
- gRefDeletedLock.unlock();
- }
- }
- }
-
- // Instead of explicitly freeing this, we're going to remove our weak and
- // strong references.
- for (; state.weakCount > 0; state.weakCount--) {
- weakRefs->decWeak(nullptr);
- }
-
- // Clean up any strong references
- for (; state.strongCount > 0; state.strongCount--) {
- ref->decStrong(nullptr);
- }
-}
-
-void spawnThreads(FuzzedDataProvider* dataProvider) {
- std::vector<std::thread> threads = std::vector<std::thread>();
-
- // Get the number of threads to generate
- uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxThreads);
- // Generate threads
- for (uint8_t i = 0; i < count; i++) {
- uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxOperations);
- std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
- std::thread tmpThread = std::thread(loop, threadOperations);
- threads.push_back(move(tmpThread));
- }
-
- for (auto& th : threads) {
- th.join();
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- gHasModifiedRefs = false;
- ref = new RefBaseSubclass(&gRefDeleted, gRefDeletedLock);
- weakRefs = ref->getWeakRefs();
- // Since we are modifying flags, (flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK
- // is true. The destructor for RefBase should clean up weakrefs because of this.
- FuzzedDataProvider dataProvider(data, size);
- spawnThreads(&dataProvider);
- LOG_ALWAYS_FATAL_IF(!gHasModifiedRefs && gRefDeleted, "ref(%p) was prematurely deleted!", ref);
- // We need to explicitly delete this object
- // if no refs have been added or deleted.
- if (!gHasModifiedRefs && !gRefDeleted) {
- delete ref;
- }
- LOG_ALWAYS_FATAL_IF(gHasModifiedRefs && !gRefDeleted,
- "ref(%p) should be deleted, is it leaking?", ref);
- return 0;
-}
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 93f9654b1..c9b489423 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -241,30 +241,6 @@ TEST(RefBase, ReplacedComparison) {
ASSERT_FALSE(wp1 != wp2);
}
-TEST(RefBase, AssertWeakRefExistsSuccess) {
- bool isDeleted;
- sp<Foo> foo = sp<Foo>::make(&isDeleted);
- wp<Foo> weakFoo = foo;
-
- EXPECT_EQ(weakFoo, wp<Foo>::fromExisting(foo.get()));
- EXPECT_EQ(weakFoo.unsafe_get(), wp<Foo>::fromExisting(foo.get()).unsafe_get());
-
- EXPECT_FALSE(isDeleted);
- foo = nullptr;
- EXPECT_TRUE(isDeleted);
-}
-
-TEST(RefBase, AssertWeakRefExistsDeath) {
- // uses some other refcounting method, or none at all
- bool isDeleted;
- Foo* foo = new Foo(&isDeleted);
-
- // can only get a valid wp<> object when you construct it from an sp<>
- EXPECT_DEATH(wp<Foo>::fromExisting(foo), "");
-
- delete foo;
-}
-
// Set up a situation in which we race with visit2AndRremove() to delete
// 2 strong references. Bar destructor checks that there are no early
// deletions and prior updates are visible to destructor.
diff --git a/libutils/SharedBuffer_test.cpp b/libutils/SharedBuffer_test.cpp
index 1d6317ff7..33a4e0c90 100644
--- a/libutils/SharedBuffer_test.cpp
+++ b/libutils/SharedBuffer_test.cpp
@@ -23,66 +23,36 @@
#include "SharedBuffer.h"
-extern "C" void __hwasan_init() __attribute__((weak));
-#define SKIP_WITH_HWASAN \
- if (&__hwasan_init != 0) GTEST_SKIP()
-
-TEST(SharedBufferTest, alloc_death) {
- EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), "");
- EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), "");
-}
-
-TEST(SharedBufferTest, alloc_max) {
- SKIP_WITH_HWASAN; // hwasan has a 2GiB allocation limit.
-
- android::SharedBuffer* buf =
- android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
- if (buf != nullptr) {
- EXPECT_NE(nullptr, buf->data());
- buf->release();
- }
-}
-
-TEST(SharedBufferTest, alloc_big) {
- SKIP_WITH_HWASAN; // hwasan has a 2GiB allocation limit.
-
- android::SharedBuffer* buf = android::SharedBuffer::alloc(SIZE_MAX / 2);
- if (buf != nullptr) {
- EXPECT_NE(nullptr, buf->data());
- buf->release();
- }
-}
-
-TEST(SharedBufferTest, alloc_zero_size) {
- android::SharedBuffer* buf = android::SharedBuffer::alloc(0);
- ASSERT_NE(nullptr, buf);
- ASSERT_EQ(0U, buf->size());
- buf->release();
-}
-
-TEST(SharedBufferTest, editResize_death) {
- android::SharedBuffer* buf = android::SharedBuffer::alloc(10);
- EXPECT_DEATH(buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer)), "");
- buf = android::SharedBuffer::alloc(10);
- EXPECT_DEATH(buf->editResize(SIZE_MAX), "");
-}
-
-TEST(SharedBufferTest, editResize_null) {
- // Big enough to fail, not big enough to abort.
- SKIP_WITH_HWASAN; // hwasan has a 2GiB allocation limit.
- android::SharedBuffer* buf = android::SharedBuffer::alloc(10);
- android::SharedBuffer* buf2 = buf->editResize(SIZE_MAX / 2);
- if (buf2 == nullptr) {
- buf->release();
- } else {
- EXPECT_NE(nullptr, buf2->data());
- buf2->release();
- }
+TEST(SharedBufferTest, TestAlloc) {
+ EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), "");
+ EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), "");
+
+ // Make sure we don't die here.
+ // Check that null is returned, as we are asking for the whole address space.
+ android::SharedBuffer* buf =
+ android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
+ ASSERT_EQ(nullptr, buf);
+
+ buf = android::SharedBuffer::alloc(0);
+ ASSERT_NE(nullptr, buf);
+ ASSERT_EQ(0U, buf->size());
+ buf->release();
}
-TEST(SharedBufferTest, editResize_zero_size) {
- android::SharedBuffer* buf = android::SharedBuffer::alloc(10);
- buf = buf->editResize(0);
- ASSERT_EQ(0U, buf->size());
- buf->release();
+TEST(SharedBufferTest, TestEditResize) {
+ android::SharedBuffer* buf = android::SharedBuffer::alloc(10);
+ EXPECT_DEATH(buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer)), "");
+ buf = android::SharedBuffer::alloc(10);
+ EXPECT_DEATH(buf->editResize(SIZE_MAX), "");
+
+ buf = android::SharedBuffer::alloc(10);
+ // Make sure we don't die here.
+ // Check that null is returned, as we are asking for the whole address space.
+ buf = buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
+ ASSERT_EQ(nullptr, buf);
+
+ buf = android::SharedBuffer::alloc(10);
+ buf = buf->editResize(0);
+ ASSERT_EQ(0U, buf->size());
+ buf->release();
}
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index 28e2d76bc..d01865e9c 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -26,26 +26,58 @@
#include <utils/Log.h>
+/*****************************************************************************/
+
namespace android {
StopWatch::StopWatch(const char* name, int clock) : mName(name), mClock(clock) {
reset();
}
-StopWatch::~StopWatch() {
- ALOGD("StopWatch %s (us): %" PRId64 " ", name(), ns2us(elapsedTime()));
+StopWatch::~StopWatch()
+{
+ nsecs_t elapsed = elapsedTime();
+ const int n = mNumLaps;
+ ALOGD("StopWatch %s (us): %" PRId64 " ", mName, ns2us(elapsed));
+ for (int i=0 ; i<n ; i++) {
+ const nsecs_t soFar = mLaps[i].soFar;
+ const nsecs_t thisLap = mLaps[i].thisLap;
+ ALOGD(" [%d: %" PRId64 ", %" PRId64, i, ns2us(soFar), ns2us(thisLap));
+ }
}
-const char* StopWatch::name() const {
+const char* StopWatch::name() const
+{
return mName;
}
-nsecs_t StopWatch::elapsedTime() const {
+nsecs_t StopWatch::lap()
+{
+ nsecs_t elapsed = elapsedTime();
+ if (mNumLaps >= 8) {
+ elapsed = 0;
+ } else {
+ const int n = mNumLaps;
+ mLaps[n].soFar = elapsed;
+ mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed;
+ mNumLaps = n+1;
+ }
+ return elapsed;
+}
+
+nsecs_t StopWatch::elapsedTime() const
+{
return systemTime(mClock) - mStartTime;
}
-void StopWatch::reset() {
+void StopWatch::reset()
+{
+ mNumLaps = 0;
mStartTime = systemTime(mClock);
}
-} // namespace android
+
+/*****************************************************************************/
+
+}; // namespace android
+
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index d08b212fd..539953a8c 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -90,6 +90,19 @@ String16::String16()
{
}
+String16::String16(StaticLinkage)
+ : mString(nullptr)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t)));
+ char16_t* data = static_cast<char16_t*>(buf->data());
+ data[0] = 0;
+ mString = data;
+}
+
String16::String16(const String16& o)
: mString(o.mString)
{
@@ -186,59 +199,99 @@ status_t String16::setTo(const char16_t* other, size_t len)
return NO_MEMORY;
}
-status_t String16::append(const String16& other) {
- return append(other.string(), other.size());
-}
-
-status_t String16::append(const char16_t* chrs, size_t otherLen) {
+status_t String16::append(const String16& other)
+{
const size_t myLen = size();
+ const size_t otherLen = other.size();
+ if (myLen == 0) {
+ setTo(other);
+ return OK;
+ } else if (otherLen == 0) {
+ return OK;
+ }
- if (myLen == 0) return setTo(chrs, otherLen);
+ if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
+ android_errorWriteLog(0x534e4554, "73826242");
+ abort();
+ }
- if (otherLen == 0) return OK;
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
+ mString = str;
+ return OK;
+ }
+ return NO_MEMORY;
+}
- size_t size = myLen;
- if (__builtin_add_overflow(size, otherLen, &size) ||
- __builtin_add_overflow(size, 1, &size) ||
- __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY;
+status_t String16::append(const char16_t* chrs, size_t otherLen)
+{
+ const size_t myLen = size();
+ if (myLen == 0) {
+ setTo(chrs, otherLen);
+ return OK;
+ } else if (otherLen == 0) {
+ return OK;
+ }
- SharedBuffer* buf = static_cast<SharedBuffer*>(editResize(size));
- if (!buf) return NO_MEMORY;
+ if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
+ android_errorWriteLog(0x534e4554, "73826242");
+ abort();
+ }
- char16_t* str = static_cast<char16_t*>(buf->data());
- memcpy(str + myLen, chrs, otherLen * sizeof(char16_t));
- str[myLen + otherLen] = 0;
- mString = str;
- return OK;
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
+ str[myLen+otherLen] = 0;
+ mString = str;
+ return OK;
+ }
+ return NO_MEMORY;
}
-status_t String16::insert(size_t pos, const char16_t* chrs) {
+status_t String16::insert(size_t pos, const char16_t* chrs)
+{
return insert(pos, chrs, strlen16(chrs));
}
-status_t String16::insert(size_t pos, const char16_t* chrs, size_t otherLen) {
+status_t String16::insert(size_t pos, const char16_t* chrs, size_t len)
+{
const size_t myLen = size();
-
- if (myLen == 0) return setTo(chrs, otherLen);
-
- if (otherLen == 0) return OK;
+ if (myLen == 0) {
+ return setTo(chrs, len);
+ return OK;
+ } else if (len == 0) {
+ return OK;
+ }
if (pos > myLen) pos = myLen;
- size_t size = myLen;
- if (__builtin_add_overflow(size, otherLen, &size) ||
- __builtin_add_overflow(size, 1, &size) ||
- __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY;
-
- SharedBuffer* buf = static_cast<SharedBuffer*>(editResize(size));
- if (!buf) return NO_MEMORY;
+ #if 0
+ printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n",
+ String8(*this).string(), pos,
+ len, myLen, String8(chrs, len).string());
+ #endif
- char16_t* str = static_cast<char16_t*>(buf->data());
- if (pos < myLen) memmove(str + pos + otherLen, str + pos, (myLen - pos) * sizeof(char16_t));
- memcpy(str + pos, chrs, otherLen * sizeof(char16_t));
- str[myLen + otherLen] = 0;
- mString = str;
- return OK;
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + len + 1) * sizeof(char16_t)));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ if (pos < myLen) {
+ memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t));
+ }
+ memcpy(str+pos, chrs, len*sizeof(char16_t));
+ str[myLen+len] = 0;
+ mString = str;
+ #if 0
+ printf("Result (%d chrs): %s\n", size(), String8(*this).string());
+ #endif
+ return OK;
+ }
+ return NO_MEMORY;
}
ssize_t String16::findFirst(char16_t c) const
@@ -350,6 +403,28 @@ size_t String16::staticStringSize() const {
return static_cast<size_t>(*(p - 1));
}
+status_t String16::makeLower()
+{
+ const size_t N = size();
+ const char16_t* str = string();
+ char16_t* edited = nullptr;
+ for (size_t i=0; i<N; i++) {
+ const char16_t v = str[i];
+ if (v >= 'A' && v <= 'Z') {
+ if (!edited) {
+ SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ edited = (char16_t*)buf->data();
+ mString = str = edited;
+ }
+ edited[i] = tolower((char)v);
+ }
+ }
+ return OK;
+}
+
status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
{
const size_t N = size();
@@ -371,4 +446,36 @@ status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
return OK;
}
+status_t String16::remove(size_t len, size_t begin)
+{
+ const size_t N = size();
+ if (begin >= N) {
+ release();
+ mString = getEmptyString();
+ return OK;
+ }
+ if (len > N || len > N - begin) len = N - begin;
+ if (begin == 0 && len == N) {
+ return OK;
+ }
+
+ if (begin > 0) {
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
+ if (!buf) {
+ return NO_MEMORY;
+ }
+ char16_t* str = (char16_t*)buf->data();
+ memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
+ mString = str;
+ }
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
+ if (buf) {
+ char16_t* str = (char16_t*)buf->data();
+ str[len] = 0;
+ mString = str;
+ return OK;
+ }
+ return NO_MEMORY;
+}
+
}; // namespace android
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
deleted file mode 100644
index d7e5ec783..000000000
--- a/libutils/String16_fuzz.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <iostream>
-
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/String16.h"
-static constexpr int MAX_STRING_BYTES = 256;
-static constexpr uint8_t MAX_OPERATIONS = 50;
-
-std::vector<std::function<void(FuzzedDataProvider&, android::String16, android::String16)>>
- operations = {
-
- // Bytes and size
- ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
- str1.string();
- }),
- ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
- str1.isStaticString();
- }),
- ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
- str1.size();
- }),
-
- // Comparison
- ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
- str1.startsWith(str2);
- }),
- ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
- str1.contains(str2.string());
- }),
- ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
- str1.compare(str2);
- }),
-
- // Append and format
- ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
- str1.append(str2);
- }),
- ([](FuzzedDataProvider& dataProvider, android::String16 str1,
- android::String16 str2) -> void {
- int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
- str1.insert(pos, str2.string());
- }),
-
- // Find and replace operations
- ([](FuzzedDataProvider& dataProvider, android::String16 str1,
- android::String16) -> void {
- char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
- str1.findFirst(findChar);
- }),
- ([](FuzzedDataProvider& dataProvider, android::String16 str1,
- android::String16) -> void {
- char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
- str1.findLast(findChar);
- }),
- ([](FuzzedDataProvider& dataProvider, android::String16 str1,
- android::String16) -> void {
- char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();
- char16_t replaceChar = dataProvider.ConsumeIntegral<char16_t>();
- str1.replaceAll(findChar, replaceChar);
- }),
-};
-
-void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1,
- android::String16 str2) {
- operations[index](dataProvider, str1, str2);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- // We're generating two char vectors.
- // First, generate lengths.
- const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
- const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
-
- // Next, populate the vectors
- std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
- std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
-
- // Get pointers to their data
- char* char_one = vec.data();
- char* char_two = vec_two.data();
-
- // Create UTF16 representations
- android::String16 str_one_utf16 = android::String16(char_one);
- android::String16 str_two_utf16 = android::String16(char_two);
-
- // Run operations against strings
- int opsRun = 0;
- while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
- uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- callFunc(op, dataProvider, str_one_utf16, str_two_utf16);
- }
-
- return 0;
-}
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index c4783211f..f1f24c394 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -19,7 +19,7 @@
#include <gtest/gtest.h>
-using namespace android;
+namespace android {
::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {
if (strcmp16(a, b) != 0) {
@@ -90,6 +90,20 @@ TEST(String16Test, Insert) {
EXPECT_STR16EQ(u"VerifyInsert me", tmp);
}
+TEST(String16Test, Remove) {
+ String16 tmp("Verify me");
+ tmp.remove(2, 6);
+ EXPECT_EQ(2U, tmp.size());
+ EXPECT_STR16EQ(u" m", tmp);
+}
+
+TEST(String16Test, MakeLower) {
+ String16 tmp("Verify Me!");
+ tmp.makeLower();
+ EXPECT_EQ(10U, tmp.size());
+ EXPECT_STR16EQ(u"verify me!", tmp);
+}
+
TEST(String16Test, ReplaceAll) {
String16 tmp("Verify verify Verify");
tmp.replaceAll(u'r', u'!');
@@ -154,6 +168,22 @@ TEST(String16Test, StaticStringInsert) {
EXPECT_FALSE(tmp.isStaticString());
}
+TEST(String16Test, StaticStringRemove) {
+ StaticString16 tmp(u"Verify me");
+ tmp.remove(2, 6);
+ EXPECT_EQ(2U, tmp.size());
+ EXPECT_STR16EQ(u" m", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringMakeLower) {
+ StaticString16 tmp(u"Verify me!");
+ tmp.makeLower();
+ EXPECT_EQ(10U, tmp.size());
+ EXPECT_STR16EQ(u"verify me!", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
TEST(String16Test, StaticStringReplaceAll) {
StaticString16 tmp(u"Verify verify Verify");
tmp.replaceAll(u'r', u'!');
@@ -185,48 +215,4 @@ TEST(String16Test, EmptyStringIsStatic) {
EXPECT_TRUE(tmp.isStaticString());
}
-TEST(String16Test, OverreadUtf8Conversion) {
- char tmp[] = {'a', static_cast<char>(0xe0), '\0'};
- String16 another(tmp);
- EXPECT_TRUE(another.size() == 0);
-}
-
-TEST(String16Test, ValidUtf8Conversion) {
- String16 another("abcdef");
- EXPECT_EQ(6U, another.size());
- EXPECT_STR16EQ(another, u"abcdef");
-}
-
-TEST(String16Test, append) {
- String16 s;
- EXPECT_EQ(OK, s.append(String16(u"foo")));
- EXPECT_STR16EQ(u"foo", s);
- EXPECT_EQ(OK, s.append(String16(u"bar")));
- EXPECT_STR16EQ(u"foobar", s);
- EXPECT_EQ(OK, s.append(u"baz", 0));
- EXPECT_STR16EQ(u"foobar", s);
- EXPECT_EQ(NO_MEMORY, s.append(u"baz", SIZE_MAX));
- EXPECT_STR16EQ(u"foobar", s);
-}
-
-TEST(String16Test, insert) {
- String16 s;
-
- // Inserting into the empty string inserts at the start.
- EXPECT_EQ(OK, s.insert(123, u"foo"));
- EXPECT_STR16EQ(u"foo", s);
-
- // Inserting zero characters at any position is okay, but won't expand the string.
- EXPECT_EQ(OK, s.insert(123, u"foo", 0));
- EXPECT_STR16EQ(u"foo", s);
-
- // Inserting past the end of a non-empty string appends.
- EXPECT_EQ(OK, s.insert(123, u"bar"));
- EXPECT_STR16EQ(u"foobar", s);
-
- EXPECT_EQ(OK, s.insert(3, u"!"));
- EXPECT_STR16EQ(u"foo!bar", s);
-
- EXPECT_EQ(NO_MEMORY, s.insert(3, u"", SIZE_MAX));
- EXPECT_STR16EQ(u"foo!bar", s);
-}
+} // namespace android
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index b391b1a18..9d50e0bc5 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -25,8 +25,6 @@
#include <ctype.h>
-#include <string>
-
#include "SharedBuffer.h"
/*
@@ -127,6 +125,19 @@ String8::String8()
{
}
+String8::String8(StaticLinkage)
+ : mString(nullptr)
+{
+ // this constructor is used when we can't rely on the static-initializers
+ // having run. In this case we always allocate an empty string. It's less
+ // efficient than using getEmptyString(), but we assume it's uncommon.
+
+ char* data = static_cast<char*>(
+ SharedBuffer::alloc(sizeof(char))->data());
+ data[0] = 0;
+ mString = data;
+}
+
String8::String8(const String8& o)
: mString(o.mString)
{
@@ -165,7 +176,9 @@ String8::String8(const char16_t* o, size_t len)
}
String8::String8(const char32_t* o)
- : mString(allocFromUTF32(o, std::char_traits<char32_t>::length(o))) {}
+ : mString(allocFromUTF32(o, strlen32(o)))
+{
+}
String8::String8(const char32_t* o, size_t len)
: mString(allocFromUTF32(o, len))
@@ -313,8 +326,8 @@ status_t String8::appendFormatV(const char* fmt, va_list args)
if (n > 0) {
size_t oldLength = length();
- if (n > std::numeric_limits<size_t>::max() - 1 ||
- oldLength > std::numeric_limits<size_t>::max() - n - 1) {
+ if ((size_t)n > SIZE_MAX - 1 ||
+ oldLength > SIZE_MAX - (size_t)n - 1) {
return NO_MEMORY;
}
char* buf = lockBuffer(oldLength + n);
@@ -327,23 +340,21 @@ status_t String8::appendFormatV(const char* fmt, va_list args)
return result;
}
-status_t String8::real_append(const char* other, size_t otherLen) {
+status_t String8::real_append(const char* other, size_t otherLen)
+{
const size_t myLen = bytes();
- SharedBuffer* buf;
- size_t newLen;
- if (__builtin_add_overflow(myLen, otherLen, &newLen) ||
- __builtin_add_overflow(newLen, 1, &newLen) ||
- (buf = SharedBuffer::bufferFromData(mString)->editResize(newLen)) == nullptr) {
- return NO_MEMORY;
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+ ->editResize(myLen+otherLen+1);
+ if (buf) {
+ char* str = (char*)buf->data();
+ mString = str;
+ str += myLen;
+ memcpy(str, other, otherLen);
+ str[otherLen] = '\0';
+ return OK;
}
-
- char* str = (char*)buf->data();
- mString = str;
- str += myLen;
- memcpy(str, other, otherLen);
- str[otherLen] = '\0';
- return OK;
+ return NO_MEMORY;
}
char* String8::lockBuffer(size_t size)
@@ -417,15 +428,50 @@ bool String8::removeAll(const char* other) {
void String8::toLower()
{
- const size_t length = size();
- if (length == 0) return;
+ toLower(0, size());
+}
+
+void String8::toLower(size_t start, size_t length)
+{
+ const size_t len = size();
+ if (start >= len) {
+ return;
+ }
+ if (start+length > len) {
+ length = len-start;
+ }
+ char* buf = lockBuffer(len);
+ buf += start;
+ while (length > 0) {
+ *buf = tolower(*buf);
+ buf++;
+ length--;
+ }
+ unlockBuffer(len);
+}
+
+void String8::toUpper()
+{
+ toUpper(0, size());
+}
- char* buf = lockBuffer(length);
- for (size_t i = length; i > 0; --i) {
- *buf = static_cast<char>(tolower(*buf));
+void String8::toUpper(size_t start, size_t length)
+{
+ const size_t len = size();
+ if (start >= len) {
+ return;
+ }
+ if (start+length > len) {
+ length = len-start;
+ }
+ char* buf = lockBuffer(len);
+ buf += start;
+ while (length > 0) {
+ *buf = toupper(*buf);
buf++;
+ length--;
}
- unlockBuffer(length);
+ unlockBuffer(len);
}
// ---------------------------------------------------------------------------
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
deleted file mode 100644
index a45d67514..000000000
--- a/libutils/String8_fuzz.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <functional>
-#include <iostream>
-#include <memory>
-
-#include "FuzzFormatTypes.h"
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/String8.h"
-
-static constexpr int MAX_STRING_BYTES = 256;
-static constexpr uint8_t MAX_OPERATIONS = 50;
-// Interestingly, 2147483614 (INT32_MAX - 33) seems to be the max value that is handled for format
-// flags. Unfortunately we need to use a smaller value so we avoid consuming too much memory.
-
-void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend);
-std::vector<std::function<void(FuzzedDataProvider*, android::String8*, android::String8*)>>
- operations = {
- // Bytes and size
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->bytes();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->isEmpty();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->length();
- },
-
- // Casing
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->toLower();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
- str1->removeAll(str2->c_str());
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
- const android::String8& constRef(*str2);
- str1->compare(constRef);
- },
-
- // Append and format
- [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
- str1->append(str2->c_str());
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1, android::String8*)
- -> void { fuzzFormat(dataProvider, str1, dataProvider->ConsumeBool()); },
-
- // Find operation
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8* str2) -> void {
- // We need to get a value from our fuzzer here.
- int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
- str1->find(str2->c_str(), start_index);
- },
-
- // Path handling
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getBasePath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathExtension();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathLeaf();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->getPathDir();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- str1->convertToResPath();
- },
- [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
- std::shared_ptr<android::String8> path_out_str =
- std::make_shared<android::String8>();
- str1->walkPath(path_out_str.get());
- path_out_str->clear();
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8*) -> void {
- str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
- },
- [](FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8*) -> void {
- str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
- },
-};
-
-void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
- FormatChar formatType = dataProvider->ConsumeEnum<FormatChar>();
-
- std::string formatString("%");
- // Width specifier
- if (dataProvider->ConsumeBool()) {
- // Left pad with zeroes
- if (dataProvider->ConsumeBool()) {
- formatString.push_back('0');
- }
- // Right justify (or left justify if negative)
- int32_t justify = dataProvider->ConsumeIntegralInRange<int32_t>(-kMaxFormatFlagValue,
- kMaxFormatFlagValue);
- formatString += std::to_string(justify);
- }
-
- // The # specifier only works with o, x, X, a, A, e, E, f, F, g, and G
- if (canApplyFlag(formatType, '#') && dataProvider->ConsumeBool()) {
- formatString.push_back('#');
- }
-
- // Precision specifier
- if (canApplyFlag(formatType, '.') && dataProvider->ConsumeBool()) {
- formatString.push_back('.');
- formatString +=
- std::to_string(dataProvider->ConsumeIntegralInRange<int>(0, kMaxFormatFlagValue));
- }
-
- formatString.push_back(kFormatChars.at(static_cast<uint8_t>(formatType)));
-
- switch (formatType) {
- case SIGNED_DECIMAL: {
- int val = dataProvider->ConsumeIntegral<int>();
- if (shouldAppend) {
- str1->appendFormat(formatString.c_str(), val);
- } else {
- str1->format(formatString.c_str(), dataProvider->ConsumeIntegral<int>());
- }
- break;
- }
-
- case UNSIGNED_DECIMAL:
- case UNSIGNED_OCTAL:
- case UNSIGNED_HEX_LOWER:
- case UNSIGNED_HEX_UPPER: {
- // Unsigned integers for u, o, x, and X
- uint val = dataProvider->ConsumeIntegral<uint>();
- if (shouldAppend) {
- str1->appendFormat(formatString.c_str(), val);
- } else {
- str1->format(formatString.c_str(), val);
- }
- break;
- }
-
- case FLOAT_LOWER:
- case FLOAT_UPPER:
- case EXPONENT_LOWER:
- case EXPONENT_UPPER:
- case SHORT_EXP_LOWER:
- case SHORT_EXP_UPPER:
- case HEX_FLOAT_LOWER:
- case HEX_FLOAT_UPPER: {
- // Floating points for f, F, e, E, g, G, a, and A
- float val = dataProvider->ConsumeFloatingPoint<float>();
- if (shouldAppend) {
- str1->appendFormat(formatString.c_str(), val);
- } else {
- str1->format(formatString.c_str(), val);
- }
- break;
- }
-
- case CHAR: {
- char val = dataProvider->ConsumeIntegral<char>();
- if (shouldAppend) {
- str1->appendFormat(formatString.c_str(), val);
- } else {
- str1->format(formatString.c_str(), val);
- }
- break;
- }
-
- case STRING: {
- std::string val = dataProvider->ConsumeRandomLengthString(MAX_STRING_BYTES);
- if (shouldAppend) {
- str1->appendFormat(formatString.c_str(), val.c_str());
- } else {
- str1->format(formatString.c_str(), val.c_str());
- }
- break;
- }
- case POINTER: {
- uintptr_t val = dataProvider->ConsumeIntegral<uintptr_t>();
- if (shouldAppend) {
- str1->appendFormat(formatString.c_str(), val);
- } else {
- str1->format(formatString.c_str(), val);
- }
- break;
- }
- }
-}
-
-void callFunc(uint8_t index, FuzzedDataProvider* dataProvider, android::String8* str1,
- android::String8* str2) {
- operations[index](dataProvider, str1, str2);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- // Generate vector lengths
- const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
- const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);
- // Populate vectors
- std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);
- std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);
- // Create UTF-8 pointers
- android::String8 str_one_utf8 = android::String8(vec.data());
- android::String8 str_two_utf8 = android::String8(vec_two.data());
- // Run operations against strings
- int opsRun = 0;
- while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
- uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
- operations[op](&dataProvider, &str_one_utf8, &str_two_utf8);
- }
- // Just to be extra sure these can be freed, we're going to explicitly clear
- // them
- str_one_utf8.clear();
- str_two_utf8.clear();
- return 0;
-}
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 1356cd08f..3947a5fb9 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -15,14 +15,13 @@
*/
#define LOG_TAG "String8_test"
-
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <gtest/gtest.h>
-using namespace android;
+namespace android {
class String8Test : public testing::Test {
protected:
@@ -97,20 +96,4 @@ TEST_F(String8Test, CheckUtf32Conversion) {
EXPECT_EQ(10U, string8.length());
}
-TEST_F(String8Test, ValidUtf16Conversion) {
- char16_t tmp[] = u"abcdef";
- String8 valid = String8(String16(tmp));
- EXPECT_STREQ(valid, "abcdef");
-}
-
-TEST_F(String8Test, append) {
- String8 s;
- EXPECT_EQ(OK, s.append("foo"));
- EXPECT_STREQ("foo", s);
- EXPECT_EQ(OK, s.append("bar"));
- EXPECT_STREQ("foobar", s);
- EXPECT_EQ(OK, s.append("baz", 0));
- EXPECT_STREQ("foobar", s);
- EXPECT_EQ(NO_MEMORY, s.append("baz", SIZE_MAX));
- EXPECT_STREQ("foobar", s);
}
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index f27c1f1cc..7b2e37f27 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -21,8 +21,8 @@
using namespace android;
-class SPFoo : virtual public RefBase {
- public:
+class SPFoo : public LightRefBase<SPFoo> {
+public:
explicit SPFoo(bool* deleted_check) : mDeleted(deleted_check) {
*mDeleted = false;
}
@@ -30,34 +30,19 @@ class SPFoo : virtual public RefBase {
~SPFoo() {
*mDeleted = true;
}
-
- private:
- bool* mDeleted;
-};
-
-class SPLightFoo : virtual public VirtualLightRefBase {
- public:
- explicit SPLightFoo(bool* deleted_check) : mDeleted(deleted_check) { *mDeleted = false; }
-
- ~SPLightFoo() { *mDeleted = true; }
-
- private:
+private:
bool* mDeleted;
};
-template <typename T>
-class StrongPointer : public ::testing::Test {};
-
-using RefBaseTypes = ::testing::Types<SPFoo, SPLightFoo>;
-TYPED_TEST_CASE(StrongPointer, RefBaseTypes);
-
-TYPED_TEST(StrongPointer, move) {
+TEST(StrongPointer, move) {
bool isDeleted;
- sp<TypeParam> sp1 = sp<TypeParam>::make(&isDeleted);
- TypeParam* foo = sp1.get();
+ SPFoo* foo = new SPFoo(&isDeleted);
+ ASSERT_EQ(0, foo->getStrongCount());
+ ASSERT_FALSE(isDeleted) << "Already deleted...?";
+ sp<SPFoo> sp1(foo);
ASSERT_EQ(1, foo->getStrongCount());
{
- sp<TypeParam> sp2 = std::move(sp1);
+ sp<SPFoo> sp2 = std::move(sp1);
ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
// The strong count isn't increasing, let's double check the old object
@@ -67,42 +52,22 @@ TYPED_TEST(StrongPointer, move) {
ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
{
// Now let's double check it deletes on time
- sp<TypeParam> sp2 = std::move(sp1);
+ sp<SPFoo> sp2 = std::move(sp1);
}
ASSERT_TRUE(isDeleted) << "foo was leaked!";
}
-TYPED_TEST(StrongPointer, NullptrComparison) {
- sp<TypeParam> foo;
+TEST(StrongPointer, NullptrComparison) {
+ sp<SPFoo> foo;
ASSERT_EQ(foo, nullptr);
ASSERT_EQ(nullptr, foo);
}
-TYPED_TEST(StrongPointer, PointerComparison) {
+TEST(StrongPointer, PointerComparison) {
bool isDeleted;
- sp<TypeParam> foo = sp<TypeParam>::make(&isDeleted);
+ sp<SPFoo> foo = new SPFoo(&isDeleted);
ASSERT_EQ(foo.get(), foo);
ASSERT_EQ(foo, foo.get());
ASSERT_NE(nullptr, foo);
ASSERT_NE(foo, nullptr);
}
-
-TYPED_TEST(StrongPointer, Deleted) {
- bool isDeleted;
- sp<TypeParam> foo = sp<TypeParam>::make(&isDeleted);
-
- auto foo2 = sp<TypeParam>::fromExisting(foo.get());
-
- EXPECT_FALSE(isDeleted);
- foo = nullptr;
- EXPECT_FALSE(isDeleted);
- foo2 = nullptr;
- EXPECT_TRUE(isDeleted);
-}
-
-TYPED_TEST(StrongPointer, AssertStrongRefExists) {
- bool isDeleted;
- TypeParam* foo = new TypeParam(&isDeleted);
- EXPECT_DEATH(sp<TypeParam>::fromExisting(foo), "");
- delete foo;
-}
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 9c71141f2..73ec1be96 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -39,15 +39,8 @@ namespace android {
*/
int64_t uptimeMillis()
{
- return nanoseconds_to_milliseconds(uptimeNanos());
-}
-
-/*
- * public static native long uptimeNanos();
- */
-int64_t uptimeNanos()
-{
- return systemTime(SYSTEM_TIME_MONOTONIC);
+ int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return (int64_t) nanoseconds_to_milliseconds(when);
}
/*
diff --git a/libutils/SystemClock_test.cpp b/libutils/SystemClock_test.cpp
index 7449dad77..5ad060b04 100644
--- a/libutils/SystemClock_test.cpp
+++ b/libutils/SystemClock_test.cpp
@@ -29,24 +29,16 @@ static const int64_t SLACK_NS = SLACK_MS * MS_IN_NS;
TEST(SystemClock, SystemClock) {
auto startUptimeMs = android::uptimeMillis();
- auto startUptimeNs = android::uptimeNanos();
auto startRealtimeMs = android::elapsedRealtime();
auto startRealtimeNs = android::elapsedRealtimeNano();
ASSERT_GT(startUptimeMs, 0)
<< "uptimeMillis() reported an impossible uptime";
- ASSERT_GT(startUptimeNs, 0)
- << "uptimeNanos() reported an impossible uptime";
ASSERT_GE(startRealtimeMs, startUptimeMs)
<< "elapsedRealtime() thinks we've suspended for negative time";
- ASSERT_GE(startRealtimeNs, startUptimeNs)
+ ASSERT_GE(startRealtimeNs, startUptimeMs * MS_IN_NS)
<< "elapsedRealtimeNano() thinks we've suspended for negative time";
- ASSERT_GE(startUptimeNs, startUptimeMs * MS_IN_NS)
- << "uptimeMillis() and uptimeNanos() are inconsistent";
- ASSERT_LT(startUptimeNs, (startUptimeMs + SLACK_MS) * MS_IN_NS)
- << "uptimeMillis() and uptimeNanos() are inconsistent";
-
ASSERT_GE(startRealtimeNs, startRealtimeMs * MS_IN_NS)
<< "elapsedRealtime() and elapsedRealtimeNano() are inconsistent";
ASSERT_LT(startRealtimeNs, (startRealtimeMs + SLACK_MS) * MS_IN_NS)
@@ -59,7 +51,6 @@ TEST(SystemClock, SystemClock) {
ASSERT_EQ(nanosleepErr, 0) << "nanosleep() failed: " << strerror(errno);
auto endUptimeMs = android::uptimeMillis();
- auto endUptimeNs = android::uptimeNanos();
auto endRealtimeMs = android::elapsedRealtime();
auto endRealtimeNs = android::elapsedRealtimeNano();
@@ -67,10 +58,6 @@ TEST(SystemClock, SystemClock) {
<< "uptimeMillis() advanced too little after nanosleep()";
EXPECT_LT(endUptimeMs - startUptimeMs, SLEEP_MS + SLACK_MS)
<< "uptimeMillis() advanced too much after nanosleep()";
- EXPECT_GE(endUptimeNs - startUptimeNs, SLEEP_NS)
- << "uptimeNanos() advanced too little after nanosleep()";
- EXPECT_LT(endUptimeNs - startUptimeNs, SLEEP_NS + SLACK_NS)
- << "uptimeNanos() advanced too much after nanosleep()";
EXPECT_GE(endRealtimeMs - startRealtimeMs, SLEEP_MS)
<< "elapsedRealtime() advanced too little after nanosleep()";
EXPECT_LT(endRealtimeMs - startRealtimeMs, SLEEP_MS + SLACK_MS)
@@ -80,11 +67,6 @@ TEST(SystemClock, SystemClock) {
EXPECT_LT(endRealtimeNs - startRealtimeNs, SLEEP_NS + SLACK_NS)
<< "elapsedRealtimeNano() advanced too much after nanosleep()";
- EXPECT_GE(endUptimeNs, endUptimeMs * MS_IN_NS)
- << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
- EXPECT_LT(endUptimeNs, (endUptimeMs + SLACK_MS) * MS_IN_NS)
- << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
-
EXPECT_GE(endRealtimeNs, endRealtimeMs * MS_IN_NS)
<< "elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()";
EXPECT_LT(endRealtimeNs, (endRealtimeMs + SLACK_MS) * MS_IN_NS)
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index fd3f4a957..1172ae784 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -20,37 +20,31 @@
#include <utils/Timers.h>
#include <limits.h>
-#include <stdlib.h>
#include <time.h>
-#include <android-base/macros.h>
-
-static constexpr size_t clock_id_max = 5;
-
-static void checkClockId(int clock) {
- if (clock < 0 || clock >= clock_id_max) abort();
-}
-
+// host linux support requires Linux 2.6.39+
#if defined(__linux__)
-nsecs_t systemTime(int clock) {
- checkClockId(clock);
- static constexpr clockid_t clocks[] = {CLOCK_REALTIME, CLOCK_MONOTONIC,
- CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID,
- CLOCK_BOOTTIME};
- static_assert(clock_id_max == arraysize(clocks));
- timespec t = {};
+nsecs_t systemTime(int clock)
+{
+ static const clockid_t clocks[] = {
+ CLOCK_REALTIME,
+ CLOCK_MONOTONIC,
+ CLOCK_PROCESS_CPUTIME_ID,
+ CLOCK_THREAD_CPUTIME_ID,
+ CLOCK_BOOTTIME
+ };
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
clock_gettime(clocks[clock], &t);
return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
}
#else
-nsecs_t systemTime(int clock) {
- // TODO: is this ever called with anything but REALTIME on mac/windows?
- checkClockId(clock);
-
+nsecs_t systemTime(int /*clock*/)
+{
// Clock support varies widely across hosts. Mac OS doesn't support
- // CLOCK_BOOTTIME (and doesn't even have clock_gettime until 10.12).
- // Windows is windows.
- timeval t = {};
+ // CLOCK_BOOTTIME, and Windows is windows.
+ struct timeval t;
+ t.tv_sec = t.tv_usec = 0;
gettimeofday(&t, nullptr);
return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
}
diff --git a/libutils/Timers_test.cpp b/libutils/Timers_test.cpp
deleted file mode 100644
index ec0051e67..000000000
--- a/libutils/Timers_test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/Timers.h>
-
-#include <gtest/gtest.h>
-
-TEST(Timers, systemTime_invalid) {
- EXPECT_EXIT(systemTime(-1), testing::KilledBySignal(SIGABRT), "");
- systemTime(SYSTEM_TIME_REALTIME);
- systemTime(SYSTEM_TIME_MONOTONIC);
- systemTime(SYSTEM_TIME_PROCESS);
- systemTime(SYSTEM_TIME_THREAD);
- systemTime(SYSTEM_TIME_BOOTTIME);
- EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), "");
-}
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 3ffcf7e34..b08e061c0 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -22,6 +22,20 @@
#include <log/log.h>
+#if defined(_WIN32)
+# undef nhtol
+# undef htonl
+# undef nhtos
+# undef htons
+
+# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
+# define htonl(x) ntohl(x)
+# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
+# define htons(x) ntohs(x)
+#else
+# include <netinet/in.h>
+#endif
+
extern "C" {
static const char32_t kByteMask = 0x000000BF;
@@ -101,6 +115,24 @@ static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size
}
}
+size_t strlen32(const char32_t *s)
+{
+ const char32_t *ss = s;
+ while ( *ss )
+ ss++;
+ return ss-s;
+}
+
+size_t strnlen32(const char32_t *s, size_t maxlen)
+{
+ const char32_t *ss = s;
+ while ((maxlen > 0) && *ss) {
+ ss++;
+ maxlen--;
+ }
+ return ss-s;
+}
+
static inline int32_t utf32_at_internal(const char* cur, size_t *num_read)
{
const char first_char = *cur;
@@ -130,9 +162,9 @@ int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t
if (index >= src_len) {
return -1;
}
- size_t unused_index;
+ size_t dummy_index;
if (next_index == nullptr) {
- next_index = &unused_index;
+ next_index = &dummy_index;
}
size_t num_read;
int32_t ret = utf32_at_internal(src + index, &num_read);
@@ -222,6 +254,19 @@ int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
return d;
}
+char16_t *strcpy16(char16_t *dst, const char16_t *src)
+{
+ char16_t *q = dst;
+ const char16_t *p = src;
+ char16_t ch;
+
+ do {
+ *q++ = ch = *p++;
+ } while ( ch );
+
+ return dst;
+}
+
size_t strlen16(const char16_t *s)
{
const char16_t *ss = s;
@@ -314,6 +359,49 @@ void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_le
// UTF-8
// --------------------------------------------------------------------------
+ssize_t utf8_length(const char *src)
+{
+ const char *cur = src;
+ size_t ret = 0;
+ while (*cur != '\0') {
+ const char first_char = *cur++;
+ if ((first_char & 0x80) == 0) { // ASCII
+ ret += 1;
+ continue;
+ }
+ // (UTF-8's character must not be like 10xxxxxx,
+ // but 110xxxxx, 1110xxxx, ... or 1111110x)
+ if ((first_char & 0x40) == 0) {
+ return -1;
+ }
+
+ int32_t mask, to_ignore_mask;
+ size_t num_to_read = 0;
+ char32_t utf32 = 0;
+ for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
+ num_to_read < 5 && (first_char & mask);
+ num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+ if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
+ return -1;
+ }
+ // 0x3F == 00111111
+ utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+ }
+ // "first_char" must be (110xxxxx - 11110xxx)
+ if (num_to_read == 5) {
+ return -1;
+ }
+ to_ignore_mask |= mask;
+ utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
+ if (utf32 > kUnicodeMaxCodepoint) {
+ return -1;
+ }
+
+ ret += num_to_read;
+ }
+ return ret;
+}
+
ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
{
if (src == nullptr || src_len == 0) {
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
deleted file mode 100644
index f6df05135..000000000
--- a/libutils/Vector_fuzz.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Vector.h"
-static constexpr uint16_t MAX_VEC_SIZE = 5000;
-
-void runVectorFuzz(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- android::Vector<uint8_t> vec = android::Vector<uint8_t>();
- // We want to test handling of sizeof as well.
- android::Vector<uint32_t> vec32 = android::Vector<uint32_t>();
-
- // We're going to generate two vectors of this size
- size_t vectorSize = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
- vec.setCapacity(vectorSize);
- vec32.setCapacity(vectorSize);
- for (size_t i = 0; i < vectorSize; i++) {
- uint8_t count = dataProvider.ConsumeIntegralInRange<uint8_t>(1, 5);
- vec.insertAt((uint8_t)i, i, count);
- vec32.insertAt((uint32_t)i, i, count);
- vec.push_front(i);
- vec32.push(i);
- }
-
- // Now we'll perform some test operations with any remaining data
- // Index to perform operations at
- size_t index = dataProvider.ConsumeIntegralInRange<size_t>(0, vec.size());
- std::vector<uint8_t> remainingVec = dataProvider.ConsumeRemainingBytes<uint8_t>();
- // Insert an array and vector
- vec.insertArrayAt(remainingVec.data(), index, remainingVec.size());
- android::Vector<uint8_t> vecCopy = android::Vector<uint8_t>(vec);
- vec.insertVectorAt(vecCopy, index);
- // Same thing for 32 bit vector
- android::Vector<uint32_t> vec32Copy = android::Vector<uint32_t>(vec32);
- vec32.insertArrayAt(vec32Copy.array(), index, vec32.size());
- vec32.insertVectorAt(vec32Copy, index);
- // Replace single character
- if (remainingVec.size() > 0) {
- vec.replaceAt(remainingVec[0], index);
- vec32.replaceAt(static_cast<uint32_t>(remainingVec[0]), index);
- } else {
- vec.replaceAt(0, index);
- vec32.replaceAt(0, index);
- }
- // Add any remaining bytes
- for (uint8_t i : remainingVec) {
- vec.add(i);
- vec32.add(static_cast<uint32_t>(i));
- }
- // Shrink capactiy
- vec.setCapacity(remainingVec.size());
- vec32.setCapacity(remainingVec.size());
- // Iterate through each pointer
- size_t sum = 0;
- for (auto& it : vec) {
- sum += it;
- }
- for (auto& it : vec32) {
- sum += it;
- }
- // Cleanup
- vec.clear();
- vecCopy.clear();
- vec32.clear();
- vec32Copy.clear();
-}
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- runVectorFuzz(data, size);
- return 0;
-}
diff --git a/libutils/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
index 0fb1a6aca..8abfb1a07 100644
--- a/libutils/include/utils/BitSet.h
+++ b/libutils/include/utils/BitSet.h
@@ -21,12 +21,9 @@
#include <utils/TypeHelpers.h>
/*
- * A class to provide efficient manipulation of bitsets.
+ * Contains some bit manipulation helpers.
*
- * Consider using std::bitset<32> or std::bitset<64> if all you want is a class to do basic bit
- * manipulation (i.e. AND / OR / XOR / flip / etc). These classes are only needed if you want to
- * efficiently perform operations like finding the first set bit in a bitset and you want to
- * avoid using the built-in functions (e.g. __builtin_clz) on std::bitset::to_ulong.
+ * DO NOT USE: std::bitset<32> or std::bitset<64> preferred
*/
namespace android {
@@ -49,9 +46,7 @@ struct BitSet32 {
// Returns the number of marked bits in the set.
inline uint32_t count() const { return count(value); }
- static inline uint32_t count(uint32_t value) {
- return static_cast<uint32_t>(__builtin_popcountl(value));
- }
+ static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return isEmpty(value); }
@@ -133,7 +128,7 @@ struct BitSet32 {
}
static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {
- return static_cast<uint32_t>(__builtin_popcountl(value & ~(0xffffffffUL >> n)));
+ return __builtin_popcountl(value & ~(0xffffffffUL >> n));
}
inline bool operator== (const BitSet32& other) const { return value == other.value; }
@@ -158,17 +153,17 @@ private:
// input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.
static inline uint32_t clz_checked(uint32_t value) {
if (sizeof(unsigned int) == sizeof(uint32_t)) {
- return static_cast<uint32_t>(__builtin_clz(value));
+ return __builtin_clz(value);
} else {
- return static_cast<uint32_t>(__builtin_clzl(value));
+ return __builtin_clzl(value);
}
}
static inline uint32_t ctz_checked(uint32_t value) {
if (sizeof(unsigned int) == sizeof(uint32_t)) {
- return static_cast<uint32_t>(__builtin_ctz(value));
+ return __builtin_ctz(value);
} else {
- return static_cast<uint32_t>(__builtin_ctzl(value));
+ return __builtin_ctzl(value);
}
}
};
@@ -193,9 +188,7 @@ struct BitSet64 {
// Returns the number of marked bits in the set.
inline uint32_t count() const { return count(value); }
- static inline uint32_t count(uint64_t value) {
- return static_cast<uint32_t>(__builtin_popcountll(value));
- }
+ static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return isEmpty(value); }
@@ -226,32 +219,26 @@ struct BitSet64 {
// Result is undefined if all bits are unmarked.
inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
- static inline uint32_t firstMarkedBit(uint64_t value) {
- return static_cast<uint32_t>(__builtin_clzll(value));
- }
+ static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); }
// Finds the first unmarked bit in the set.
// Result is undefined if all bits are marked.
inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
- static inline uint32_t firstUnmarkedBit(uint64_t value) {
- return static_cast<uint32_t>(__builtin_clzll(~value));
- }
+ static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); }
// Finds the last marked bit in the set.
// Result is undefined if all bits are unmarked.
inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
- static inline uint32_t lastMarkedBit(uint64_t value) {
- return static_cast<uint32_t>(63 - __builtin_ctzll(value));
- }
+ static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); }
// Finds the first marked bit in the set and clears it. Returns the bit index.
// Result is undefined if all bits are unmarked.
inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
static inline uint32_t clearFirstMarkedBit(uint64_t& value) {
- uint32_t n = firstMarkedBit(value);
+ uint64_t n = firstMarkedBit(value);
clearBit(value, n);
return n;
}
@@ -261,7 +248,7 @@ struct BitSet64 {
inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {
- uint32_t n = firstUnmarkedBit(value);
+ uint64_t n = firstUnmarkedBit(value);
markBit(value, n);
return n;
}
@@ -271,7 +258,7 @@ struct BitSet64 {
inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
static inline uint32_t clearLastMarkedBit(uint64_t& value) {
- uint32_t n = lastMarkedBit(value);
+ uint64_t n = lastMarkedBit(value);
clearBit(value, n);
return n;
}
@@ -281,7 +268,7 @@ struct BitSet64 {
inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }
static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {
- return static_cast<uint32_t>(__builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)));
+ return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n));
}
inline bool operator== (const BitSet64& other) const { return value == other.value; }
diff --git a/libutils/include/utils/Debug.h b/libutils/include/utils/Debug.h
index c3b902617..96bd70eb0 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -14,9 +14,27 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_UTILS_DEBUG_H
+#define ANDROID_UTILS_DEBUG_H
-// Note: new code should use static_assert directly.
+#include <stdint.h>
+#include <sys/types.h>
-#define COMPILE_TIME_ASSERT static_assert
-#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE static_assert
+namespace android {
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+#define COMPILE_TIME_ASSERT(_exp) \
+ template class CompileTimeAssert< (_exp) >;
+#endif
+
+// DO NOT USE: Please use static_assert instead
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
+ CompileTimeAssert<( _exp )>();
+
+// ---------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_UTILS_DEBUG_H
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 8aa381a8f..17c5e1057 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_UTILS_FLATTENABLE_H
+#define ANDROID_UTILS_FLATTENABLE_H
// DO NOT USE: please use parcelable instead
// This code is deprecated and will not be supported via AIDL code gen. For data
@@ -24,6 +25,7 @@
#include <string.h>
#include <sys/types.h>
#include <utils/Errors.h>
+#include <utils/Debug.h>
#include <type_traits>
@@ -215,3 +217,5 @@ public:
};
} // namespace android
+
+#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
index 5cf7a1165..7bda99beb 100644
--- a/libutils/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -47,7 +47,7 @@ public:
inline void clear() { mVector.clear(); }
- /*!
+ /*!
* vector stats
*/
@@ -63,14 +63,14 @@ public:
// returns true if the arguments is known to be identical to this vector
inline bool isIdenticalTo(const KeyedVector& rhs) const;
- /*!
+ /*!
* accessors
*/
- const VALUE& valueFor(const KEY& key) const;
- const VALUE& valueAt(size_t index) const;
- const KEY& keyAt(size_t index) const;
- ssize_t indexOfKey(const KEY& key) const;
- const VALUE& operator[](size_t index) const;
+ const VALUE& valueFor(const KEY& key) const;
+ const VALUE& valueAt(size_t index) const;
+ const KEY& keyAt(size_t index) const;
+ ssize_t indexOfKey(const KEY& key) const;
+ const VALUE& operator[] (size_t index) const;
/*!
* modifying the array
@@ -79,10 +79,10 @@ public:
VALUE& editValueFor(const KEY& key);
VALUE& editValueAt(size_t index);
- /*!
+ /*!
* add/insert/replace items
*/
-
+
ssize_t add(const KEY& key, const VALUE& item);
ssize_t replaceValueFor(const KEY& key, const VALUE& item);
ssize_t replaceValueAt(size_t index, const VALUE& item);
@@ -93,7 +93,7 @@ public:
ssize_t removeItem(const KEY& key);
ssize_t removeItemsAt(size_t index, size_t count = 1);
-
+
private:
SortedVector< key_value_pair_t<KEY, VALUE> > mVector;
};
@@ -208,7 +208,7 @@ DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
template<typename KEY, typename VALUE> inline
const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
ssize_t i = this->indexOfKey(key);
- return i >= 0 ? KeyedVector<KEY, VALUE>::valueAt(static_cast<size_t>(i)) : mDefault;
+ return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
}
} // namespace android
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
index 40edf6718..b04e5c155 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -28,8 +28,6 @@ namespace android {
class ReferenceRenamer;
-void LightRefBase_reportIncStrongRequireStrongFailed(const void* thiz);
-
template <class T>
class LightRefBase
{
@@ -38,11 +36,6 @@ public:
inline void incStrong(__attribute__((unused)) const void* id) const {
mCount.fetch_add(1, std::memory_order_relaxed);
}
- inline void incStrongRequireStrong(__attribute__((unused)) const void* id) const {
- if (0 == mCount.fetch_add(1, std::memory_order_relaxed)) {
- LightRefBase_reportIncStrongRequireStrongFailed(this);
- }
- }
inline void decStrong(__attribute__((unused)) const void* id) const {
if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
@@ -66,6 +59,7 @@ private:
mutable std::atomic<int32_t> mCount;
};
+
// This is a wrapper around LightRefBase that simply enforces a virtual
// destructor to eliminate the template requirement of LightRefBase
class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index 466fbb726..c439c5ce0 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -26,8 +26,6 @@
#include <android-base/unique_fd.h>
-#include <utility>
-
namespace android {
/*
@@ -440,8 +438,9 @@ private:
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
- MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
- : uptime(u), handler(std::move(h)), message(m) {}
+ MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
+ const Message& m) : uptime(u), handler(h), message(m) {
+ }
nsecs_t uptime;
sp<MessageHandler> handler;
diff --git a/libutils/include/utils/PropertyMap.h b/libutils/include/utils/PropertyMap.h
new file mode 100644
index 000000000..a9e674f9a
--- /dev/null
+++ b/libutils/include/utils/PropertyMap.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+ /* Creates an empty property map. */
+ PropertyMap();
+ ~PropertyMap();
+
+ /* Clears the property map. */
+ void clear();
+
+ /* Adds a property.
+ * Replaces the property with the same key if it is already present.
+ */
+ void addProperty(const String8& key, const String8& value);
+
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const String8& key) const;
+
+ /* Gets the value of a property and parses it.
+ * Returns true and sets outValue if the key was found and its value was parsed successfully.
+ * Otherwise returns false and does not modify outValue. (Also logs a warning.)
+ */
+ bool tryGetProperty(const String8& key, String8& outValue) const;
+ bool tryGetProperty(const String8& key, bool& outValue) const;
+ bool tryGetProperty(const String8& key, int32_t& outValue) const;
+ bool tryGetProperty(const String8& key, float& outValue) const;
+
+ /* Adds all values from the specified property map. */
+ void addAll(const PropertyMap* map);
+
+ /* Gets the underlying property map. */
+ inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
+
+ /* Loads a property map from a file. */
+ static status_t load(const String8& filename, PropertyMap** outMap);
+
+private:
+ class Parser {
+ PropertyMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(PropertyMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseType();
+ status_t parseKey();
+ status_t parseKeyProperty();
+ status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseCharacterLiteral(char16_t* outCharacter);
+ };
+
+ KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e07f57415..89f048db8 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -140,9 +140,7 @@
// count, and accidentally passed to f(sp<T>), a strong pointer to the object
// will be temporarily constructed and destroyed, prematurely deallocating the
// object, and resulting in heap corruption. None of this would be easily
-// visible in the source. See below on
-// ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION for a compile time
-// option which helps avoid this case.
+// visible in the source.
// Extra Features:
@@ -169,42 +167,6 @@
// to THE SAME sp<> or wp<>. In effect, their thread-safety properties are
// exactly like those of T*, NOT atomic<T*>.
-// Safety option: ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION
-//
-// This flag makes the semantics for using a RefBase object with wp<> and sp<>
-// much stricter by disabling implicit conversion from raw pointers to these
-// objects. In order to use this, apply this flag in Android.bp like so:
-//
-// cflags: [
-// "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
-// ],
-//
-// REGARDLESS of whether this flag is on, best usage of sp<> is shown below. If
-// this flag is on, no other usage is possible (directly calling RefBase methods
-// is possible, but seeing code using 'incStrong' instead of 'sp<>', for
-// instance, should already set off big alarm bells. With carefully constructed
-// data structures, it should NEVER be necessary to directly use RefBase
-// methods). Proper RefBase usage:
-//
-// class Foo : virtual public RefBase { ... };
-//
-// // always construct an sp object with sp::make
-// sp<Foo> myFoo = sp<Foo>::make(/*args*/);
-//
-// // if you need a weak pointer, it must be constructed from a strong
-// // pointer
-// wp<Foo> weakFoo = myFoo; // NOT myFoo.get()
-//
-// // If you are inside of a method of Foo and need access to a strong
-// // explicitly call this function. This documents your intention to code
-// // readers, and it will give a runtime error for what otherwise would
-// // be potential double ownership
-// .... Foo::someMethod(...) {
-// // asserts if there is a memory issue
-// sp<Foo> thiz = sp<Foo>::fromExisting(this);
-// }
-//
-
#ifndef ANDROID_REF_BASE_H
#define ANDROID_REF_BASE_H
@@ -282,7 +244,6 @@ class RefBase
{
public:
void incStrong(const void* id) const;
- void incStrongRequireStrong(const void* id) const;
void decStrong(const void* id) const;
void forceIncStrong(const void* id) const;
@@ -296,7 +257,6 @@ public:
RefBase* refBase() const;
void incWeak(const void* id);
- void incWeakRequireWeak(const void* id);
void decWeak(const void* id);
// acquires a strong reference if there is already one.
@@ -337,11 +297,6 @@ public:
}
protected:
- // When constructing these objects, prefer using sp::make<>. Using a RefBase
- // object on the stack or with other refcount mechanisms (e.g.
- // std::shared_ptr) is inherently wrong. RefBase types have an implicit
- // ownership model and cannot be safely used with other ownership models.
-
RefBase();
virtual ~RefBase();
@@ -405,27 +360,10 @@ public:
inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
- // if nullptr, returns nullptr
- //
- // if a weak pointer is already available, this will retrieve it,
- // otherwise, this will abort
- static inline wp<T> fromExisting(T* other);
-
- // for more information about this flag, see above
-#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
- wp(std::nullptr_t) : wp() {}
-#else
wp(T* other); // NOLINT(implicit)
- template <typename U>
- wp(U* other); // NOLINT(implicit)
- wp& operator=(T* other);
- template <typename U>
- wp& operator=(U* other);
-#endif
-
wp(const wp<T>& other);
explicit wp(const sp<T>& other);
-
+ template<typename U> wp(U* other); // NOLINT(implicit)
template<typename U> wp(const sp<U>& other); // NOLINT(implicit)
template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
@@ -433,9 +371,11 @@ public:
// Assignment
+ wp& operator = (T* other);
wp& operator = (const wp<T>& other);
wp& operator = (const sp<T>& other);
+ template<typename U> wp& operator = (U* other);
template<typename U> wp& operator = (const wp<U>& other);
template<typename U> wp& operator = (const sp<U>& other);
@@ -536,20 +476,6 @@ private:
// Note that the above comparison operations go out of their way to provide an ordering consistent
// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
-template <typename T>
-wp<T> wp<T>::fromExisting(T* other) {
- if (!other) return nullptr;
-
- auto refs = other->getWeakRefs();
- refs->incWeakRequireWeak(other);
-
- wp<T> ret;
- ret.m_ptr = other;
- ret.m_refs = refs;
- return ret;
-}
-
-#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
@@ -557,32 +483,6 @@ wp<T>::wp(T* other)
m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}
-template <typename T>
-template <typename U>
-wp<T>::wp(U* other) : m_ptr(other) {
- m_refs = other ? other->createWeak(this) : nullptr;
-}
-
-template <typename T>
-wp<T>& wp<T>::operator=(T* other) {
- weakref_type* newRefs = other ? other->createWeak(this) : nullptr;
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = other;
- m_refs = newRefs;
- return *this;
-}
-
-template <typename T>
-template <typename U>
-wp<T>& wp<T>::operator=(U* other) {
- weakref_type* newRefs = other ? other->createWeak(this) : 0;
- if (m_ptr) m_refs->decWeak(this);
- m_ptr = other;
- m_refs = newRefs;
- return *this;
-}
-#endif
-
template<typename T>
wp<T>::wp(const wp<T>& other)
: m_ptr(other.m_ptr), m_refs(other.m_refs)
@@ -598,6 +498,13 @@ wp<T>::wp(const sp<T>& other)
}
template<typename T> template<typename U>
+wp<T>::wp(U* other)
+ : m_ptr(other)
+{
+ m_refs = other ? other->createWeak(this) : nullptr;
+}
+
+template<typename T> template<typename U>
wp<T>::wp(const wp<U>& other)
: m_ptr(other.m_ptr)
{
@@ -623,6 +530,17 @@ wp<T>::~wp()
}
template<typename T>
+wp<T>& wp<T>::operator = (T* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : nullptr;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T>
wp<T>& wp<T>::operator = (const wp<T>& other)
{
weakref_type* otherRefs(other.m_refs);
@@ -647,6 +565,17 @@ wp<T>& wp<T>::operator = (const sp<T>& other)
}
template<typename T> template<typename U>
+wp<T>& wp<T>::operator = (U* other)
+{
+ weakref_type* newRefs =
+ other ? other->createWeak(this) : 0;
+ if (m_ptr) m_refs->decWeak(this);
+ m_ptr = other;
+ m_refs = newRefs;
+ return *this;
+}
+
+template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const wp<U>& other)
{
weakref_type* otherRefs(other.m_refs);
diff --git a/libutils/include/utils/StopWatch.h b/libutils/include/utils/StopWatch.h
index 4e53edaab..9b14ac809 100644
--- a/libutils/include/utils/StopWatch.h
+++ b/libutils/include/utils/StopWatch.h
@@ -14,30 +14,46 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_STOPWATCH_H
+#define ANDROID_STOPWATCH_H
#include <stdint.h>
#include <sys/types.h>
#include <utils/Timers.h>
-namespace android {
-
-class StopWatch {
- public:
- StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);
- ~StopWatch();
-
- const char* name() const;
- nsecs_t elapsedTime() const;
-
- void reset();
+// ---------------------------------------------------------------------------
- private:
- const char* mName;
- int mClock;
+namespace android {
- nsecs_t mStartTime;
+class StopWatch
+{
+public:
+ StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);
+ ~StopWatch();
+
+ const char* name() const;
+ nsecs_t lap();
+ nsecs_t elapsedTime() const;
+
+ void reset();
+
+private:
+ const char* mName;
+ int mClock;
+
+ struct lap_t {
+ nsecs_t soFar;
+ nsecs_t thisLap;
+ };
+
+ nsecs_t mStartTime;
+ lap_t mLaps[8];
+ int mNumLaps;
};
} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_STOPWATCH_H
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 60d523a4c..c0e3f1eba 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -39,7 +39,17 @@ class StaticString16;
class String16
{
public:
+ /*
+ * Use String16(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String16, e.g.:
+ *
+ * static String16 sAStaticEmptyString(String16::kEmptyString);
+ * static String16 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
String16();
+ explicit String16(StaticLinkage);
String16(const String16& o);
String16(const String16& o,
size_t len,
@@ -85,9 +95,13 @@ public:
bool contains(const char16_t* chrs) const;
+ status_t makeLower();
+
status_t replaceAll(char16_t replaceThis,
char16_t withThis);
+ status_t remove(size_t len, size_t begin=0);
+
inline int compare(const String16& other) const;
inline bool operator<(const String16& other) const;
@@ -183,7 +197,7 @@ public:
ANDROID_TRIVIAL_MOVE_TRAIT(String16)
static inline std::ostream& operator<<(std::ostream& os, const String16& str) {
- os << String8(str);
+ os << String8(str).c_str();
return os;
}
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index cee5dc640..0ddcbb2be 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -17,8 +17,7 @@
#ifndef ANDROID_STRING8_H
#define ANDROID_STRING8_H
-#include <iostream>
-#include <string>
+#include <string> // for std::string
#include <utils/Errors.h>
#include <utils/Unicode.h>
@@ -40,7 +39,16 @@ class String16;
class String8
{
public:
+ /* use String8(StaticLinkage) if you're statically linking against
+ * libutils and declaring an empty static String8, e.g.:
+ *
+ * static String8 sAStaticEmptyString(String8::kEmptyString);
+ * static String8 sAnotherStaticEmptyString(sAStaticEmptyString);
+ */
+ enum StaticLinkage { kEmptyString };
+
String8();
+ explicit String8(StaticLinkage);
String8(const String8& o);
explicit String8(const char* o);
explicit String8(const char* o, size_t numChars);
@@ -130,6 +138,9 @@ public:
bool removeAll(const char* other);
void toLower();
+ void toLower(size_t start, size_t numChars);
+ void toUpper();
+ void toUpper(size_t start, size_t numChars);
/*
@@ -230,11 +241,6 @@ private:
// require any change to the underlying SharedBuffer contents or reference count.
ANDROID_TRIVIAL_MOVE_TRAIT(String8)
-static inline std::ostream& operator<<(std::ostream& os, const String8& str) {
- os << str.c_str();
- return os;
-}
-
// ---------------------------------------------------------------------------
// No user servicable parts below.
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index bb1941b79..6f4fb4788 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -32,64 +32,24 @@ class sp {
public:
inline sp() : m_ptr(nullptr) { }
- // The old way of using sp<> was like this. This is bad because it relies
- // on implicit conversion to sp<>, which we would like to remove (if an
- // object is being managed some other way, this is double-ownership). We
- // want to move away from this:
- //
- // sp<Foo> foo = new Foo(...); // DO NOT DO THIS
- //
- // Instead, prefer to do this:
- //
- // sp<Foo> foo = sp<Foo>::make(...); // DO THIS
- //
- // Sometimes, in order to use this, when a constructor is marked as private,
- // you may need to add this to your class:
- //
- // friend class sp<Foo>;
- template <typename... Args>
- static inline sp<T> make(Args&&... args);
-
- // if nullptr, returns nullptr
- //
- // if a strong pointer is already available, this will retrieve it,
- // otherwise, this will abort
- static inline sp<T> fromExisting(T* other);
-
- // for more information about this macro and correct RefBase usage, see
- // the comment at the top of utils/RefBase.h
-#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
- sp(std::nullptr_t) : sp() {}
-#else
sp(T* other); // NOLINT(implicit)
- template <typename U>
- sp(U* other); // NOLINT(implicit)
- sp& operator=(T* other);
- template <typename U>
- sp& operator=(U* other);
-#endif
-
sp(const sp<T>& other);
sp(sp<T>&& other) noexcept;
-
+ template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
- // Cast a strong pointer directly from one type to another. Constructors
- // allow changing types, but only if they are pointer-compatible. This does
- // a static_cast internally.
- template <typename U>
- static inline sp<T> cast(const sp<U>& other);
-
~sp();
// Assignment
+ sp& operator = (T* other);
sp& operator = (const sp<T>& other);
sp& operator=(sp<T>&& other) noexcept;
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
+ template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
@@ -200,6 +160,9 @@ void sp_report_stack_pointer();
// It does not appear safe to broaden this check to include adjacent pages; apparently this code
// is used in environments where there may not be a guard page below (at higher addresses than)
// the bottom of the stack.
+//
+// TODO: Consider adding make_sp<T>() to allocate an object and wrap the resulting pointer safely
+// without checking overhead.
template <typename T>
void sp<T>::check_not_on_stack(const void* ptr) {
static constexpr int MIN_PAGE_SIZE = 0x1000; // 4K. Safer than including sys/user.h.
@@ -211,31 +174,6 @@ void sp<T>::check_not_on_stack(const void* ptr) {
}
}
-// TODO: Ideally we should find a way to increment the reference count before running the
-// constructor, so that generating an sp<> to this in the constructor is no longer dangerous.
-template <typename T>
-template <typename... Args>
-sp<T> sp<T>::make(Args&&... args) {
- T* t = new T(std::forward<Args>(args)...);
- sp<T> result;
- result.m_ptr = t;
- t->incStrong(t); // bypass check_not_on_stack for heap allocation
- return result;
-}
-
-template <typename T>
-sp<T> sp<T>::fromExisting(T* other) {
- if (other) {
- check_not_on_stack(other);
- other->incStrongRequireStrong(other);
- sp<T> result;
- result.m_ptr = other;
- return result;
- }
- return nullptr;
-}
-
-#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
@@ -245,29 +183,6 @@ sp<T>::sp(T* other)
}
}
-template <typename T>
-template <typename U>
-sp<T>::sp(U* other) : m_ptr(other) {
- if (other) {
- check_not_on_stack(other);
- (static_cast<T*>(other))->incStrong(this);
- }
-}
-
-template <typename T>
-sp<T>& sp<T>::operator=(T* other) {
- T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
- if (other) {
- check_not_on_stack(other);
- other->incStrong(this);
- }
- if (oldPtr) oldPtr->decStrong(this);
- if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
- m_ptr = other;
- return *this;
-}
-#endif
-
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr) {
@@ -281,6 +196,15 @@ sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
}
template<typename T> template<typename U>
+sp<T>::sp(U* other)
+ : m_ptr(other) {
+ if (other) {
+ check_not_on_stack(other);
+ (static_cast<T*>(other))->incStrong(this);
+ }
+}
+
+template<typename T> template<typename U>
sp<T>::sp(const sp<U>& other)
: m_ptr(other.m_ptr) {
if (m_ptr)
@@ -293,12 +217,6 @@ sp<T>::sp(sp<U>&& other)
other.m_ptr = nullptr;
}
-template <typename T>
-template <typename U>
-sp<T> sp<T>::cast(const sp<U>& other) {
- return sp<T>::fromExisting(static_cast<T*>(other.get()));
-}
-
template<typename T>
sp<T>::~sp() {
if (m_ptr)
@@ -327,6 +245,19 @@ sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
return *this;
}
+template<typename T>
+sp<T>& sp<T>::operator =(T* other) {
+ T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
+ if (other) {
+ check_not_on_stack(other);
+ other->incStrong(this);
+ }
+ if (oldPtr) oldPtr->decStrong(this);
+ if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
+ m_ptr = other;
+ return *this;
+}
+
template<typename T> template<typename U>
sp<T>& sp<T>::operator =(const sp<U>& other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
@@ -348,7 +279,6 @@ sp<T>& sp<T>::operator =(sp<U>&& other) {
return *this;
}
-#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)
template<typename T> template<typename U>
sp<T>& sp<T>::operator =(U* other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
@@ -358,7 +288,6 @@ sp<T>& sp<T>::operator =(U* other) {
m_ptr = other;
return *this;
}
-#endif
template<typename T>
void sp<T>::force_set(T* other) {
diff --git a/libutils/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
index 3c59297a6..f816fbaf7 100644
--- a/libutils/include/utils/SystemClock.h
+++ b/libutils/include/utils/SystemClock.h
@@ -20,18 +20,10 @@
#include <stdint.h>
#include <sys/types.h>
-// See https://developer.android.com/reference/android/os/SystemClock
-// to learn more about Android's timekeeping facilities.
-
namespace android {
-// Returns milliseconds since boot, not counting time spent in deep sleep.
int64_t uptimeMillis();
-// Returns nanoseconds since boot, not counting time spent in deep sleep.
-int64_t uptimeNanos();
-// Returns milliseconds since boot, including time spent in sleep.
int64_t elapsedRealtime();
-// Returns nanoseconds since boot, including time spent in sleep.
int64_t elapsedRealtimeNano();
} // namespace android
diff --git a/libutils/include/utils/Timers.h b/libutils/include/utils/Timers.h
index 197fc26b8..54ec47489 100644
--- a/libutils/include/utils/Timers.h
+++ b/libutils/include/utils/Timers.h
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-#pragma once
+//
+// Timer functions.
+//
+#ifndef _LIBS_UTILS_TIMERS_H
+#define _LIBS_UTILS_TIMERS_H
#include <stdint.h>
#include <sys/types.h>
@@ -73,11 +77,11 @@ static CONSTEXPR inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
static CONSTEXPR inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }
enum {
- SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
- SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
- SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
- SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
- SYSTEM_TIME_BOOTTIME = 4, // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
+ SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock
+ SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
+ SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock
+ SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock
+ SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time
};
// return the system-time according to the specified clock
@@ -100,3 +104,5 @@ int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);
#ifdef __cplusplus
} // extern "C"
#endif
+
+#endif // _LIBS_UTILS_TIMERS_H
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index d60d5d6ba..fc6712d9b 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -27,6 +27,7 @@ int strcmp16(const char16_t *, const char16_t *);
int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
size_t strlen16(const char16_t *);
size_t strnlen16(const char16_t *, size_t);
+char16_t *strcpy16(char16_t *, const char16_t *);
char16_t *strstr16(const char16_t*, const char16_t*);
// Version of comparison that supports embedded NULs.
@@ -38,6 +39,10 @@ char16_t *strstr16(const char16_t*, const char16_t*);
// equivalent result as strcmp16 (unlike strncmp16).
int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
+// Standard string functions on char32_t strings.
+size_t strlen32(const char32_t *);
+size_t strnlen32(const char32_t *, size_t);
+
/**
* Measure the length of a UTF-32 string in UTF-8. If the string is invalid
* such as containing a surrogate character, -1 will be returned.
@@ -106,6 +111,24 @@ ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
/**
+ * Returns the length of "src" when "src" is valid UTF-8 string.
+ * Returns 0 if src is NULL or 0-length string. Returns -1 when the source
+ * is an invalid string.
+ *
+ * This function should be used to determine whether "src" is valid UTF-8
+ * characters with valid unicode codepoints. "src" must be nul-terminated.
+ *
+ * If you are going to use other utf8_to_... functions defined in this header
+ * with string which may not be valid UTF-8 with valid codepoint (form 0 to
+ * 0x10FFFF), you should use this function before calling others, since the
+ * other functions do not check whether the string is valid UTF-8 or not.
+ *
+ * If you do not care whether "src" is valid UTF-8 or not, you should use
+ * strlen() as usual, which should be much faster.
+ */
+ssize_t utf8_length(const char *src);
+
+/**
* Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
* it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
* can ask to log a message and fail in case the invalid utf8 could have caused an override if no
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index be35ea2f0..ddf71de24 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -23,13 +23,15 @@
#include <log/log.h>
#include <utils/TypeHelpers.h>
#include <utils/VectorImpl.h>
+
+/*
+ * Used to blacklist some functions from CFI.
+ *
+ */
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
-/*
- * Used to exclude some functions from CFI.
- */
#if __has_attribute(no_sanitize)
#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi")))
#else
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index f800bf7a0..b92c76c9f 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -1,13 +1,6 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library {
name: "libvndksupport",
native_bridge_supported: true,
- llndk: {
- symbol_file: "libvndksupport.map.txt",
- },
srcs: ["linker.cpp"],
cflags: [
"-Wall",
@@ -25,3 +18,10 @@ cc_library {
versions: ["29"],
},
}
+
+llndk_library {
+ name: "libvndksupport",
+ native_bridge_supported: true,
+ symbol_file: "libvndksupport.map.txt",
+ export_include_dirs: ["include"],
+}
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index ba700cbb9..2570cce95 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_test {
name: "libvndksupport-tests",
srcs: [
diff --git a/libziparchive/.clang-format b/libziparchive/.clang-format
new file mode 120000
index 000000000..fd0645fdf
--- /dev/null
+++ b/libziparchive/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2 \ No newline at end of file
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
new file mode 100644
index 000000000..553136af8
--- /dev/null
+++ b/libziparchive/Android.bp
@@ -0,0 +1,218 @@
+//
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+ name: "libziparchive_flags",
+ cflags: [
+ // ZLIB_CONST turns on const for input buffers, which is pretty standard.
+ "-DZLIB_CONST",
+ "-Werror",
+ "-Wall",
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ cppflags: [
+ // Incorrectly warns when C++11 empty brace {} initializer is used.
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
+ "-Wno-missing-field-initializers",
+ "-Wconversion",
+ "-Wno-sign-conversion",
+ ],
+
+ // Enable -Wold-style-cast only for non-Windows targets. _islower_l,
+ // _isupper_l etc. in MinGW locale_win32.h (included from
+ // libcxx/include/__locale) has an old-style-cast.
+ target: {
+ not_windows: {
+ cppflags: [
+ "-Wold-style-cast",
+ ],
+ },
+ },
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ "shift",
+ "integer-divide-by-zero",
+ "implicit-signed-integer-truncation",
+ // TODO: Fix crash when we enable this option
+ // "implicit-unsigned-integer-truncation",
+ // TODO: not tested yet.
+ // "implicit-integer-sign-change",
+ ],
+ },
+}
+
+cc_defaults {
+ name: "libziparchive_defaults",
+ srcs: [
+ "zip_archive.cc",
+ "zip_archive_stream_entry.cc",
+ "zip_writer.cc",
+ ],
+
+ target: {
+ windows: {
+ cflags: ["-mno-ms-bitfields"],
+
+ enabled: true,
+ },
+ },
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ // for FRIEND_TEST
+ static_libs: ["libgtest_prod"],
+ export_static_lib_headers: ["libgtest_prod"],
+
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libziparchive",
+ host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ export_shared_lib_headers: ["libbase"],
+
+ defaults: [
+ "libziparchive_defaults",
+ "libziparchive_flags",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ "libz",
+ ],
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.art.debug",
+ "com.android.art.release",
+ ],
+}
+
+// Tests.
+cc_test {
+ name: "ziparchive-tests",
+ host_supported: true,
+ defaults: ["libziparchive_flags"],
+
+ data: [
+ "testdata/**/*",
+ ],
+
+ srcs: [
+ "entry_name_utils_test.cc",
+ "zip_archive_test.cc",
+ "zip_writer_test.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libziparchive",
+ "libz",
+ "libutils",
+ ],
+
+ target: {
+ host: {
+ cppflags: ["-Wno-unnamed-type-template-args"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+ test_suites: ["device-tests"],
+}
+
+// Performance benchmarks.
+cc_benchmark {
+ name: "ziparchive-benchmarks",
+ defaults: ["libziparchive_flags"],
+
+ srcs: [
+ "zip_archive_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libziparchive",
+ "libz",
+ "libutils",
+ ],
+
+ target: {
+ host: {
+ cppflags: ["-Wno-unnamed-type-template-args"],
+ },
+ },
+}
+
+cc_binary {
+ name: "ziptool",
+ defaults: ["libziparchive_flags"],
+ srcs: ["ziptool.cpp"],
+ shared_libs: [
+ "libbase",
+ "libziparchive",
+ ],
+ recovery_available: true,
+ host_supported: true,
+ target: {
+ android: {
+ symlinks: ["unzip", "zipinfo"],
+ },
+ },
+}
+
+cc_fuzz {
+ name: "libziparchive_fuzzer",
+ srcs: ["libziparchive_fuzzer.cpp"],
+ static_libs: ["libziparchive", "libbase", "libz", "liblog"],
+ host_supported: true,
+ corpus: ["testdata/*"],
+}
+
+sh_test {
+ name: "ziptool-tests",
+ src: "run-ziptool-tests-on-android.sh",
+ filename: "run-ziptool-tests-on-android.sh",
+ test_suites: ["general-tests"],
+ host_supported: true,
+ device_supported: false,
+ test_config: "ziptool-tests.xml",
+ data: ["cli-tests/**/*"],
+ target_required: ["cli-test", "ziptool"],
+}
diff --git a/libziparchive/OWNERS b/libziparchive/OWNERS
new file mode 100644
index 000000000..fcc567aa2
--- /dev/null
+++ b/libziparchive/OWNERS
@@ -0,0 +1 @@
+narayan@google.com
diff --git a/libziparchive/cli-tests/files/example.zip b/libziparchive/cli-tests/files/example.zip
new file mode 100644
index 000000000..c3292e929
--- /dev/null
+++ b/libziparchive/cli-tests/files/example.zip
Binary files differ
diff --git a/libziparchive/cli-tests/unzip.test b/libziparchive/cli-tests/unzip.test
new file mode 100755
index 000000000..6e5cbf213
--- /dev/null
+++ b/libziparchive/cli-tests/unzip.test
@@ -0,0 +1,148 @@
+# unzip tests.
+
+# Note: since "master key", Android uses libziparchive for all zip file
+# handling, and that scans the whole central directory immediately. Not only
+# lookups by name but also iteration is implemented using the resulting hash
+# table, meaning that any test that makes assumptions about iteration order
+# will fail on Android.
+
+name: unzip -l
+command: unzip -l $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Date Time Name
+ --------- ---------- ----- ----
+ 1024 2017-06-04 08:45 d1/d2/x.txt
+ --------- -------
+ 1024 1 file
+---
+
+name: unzip -lq
+command: unzip -lq $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/x.txt ]
+expected-stdout:
+ Length Date Time Name
+ --------- ---------- ----- ----
+ 1024 2017-06-04 08:45 d1/d2/x.txt
+ --------- -------
+ 1024 1 file
+---
+
+name: unzip -lv
+command: unzip -lv $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/file ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Method Size Cmpr Date Time CRC-32 Name
+ -------- ------ ------- ---- ---------- ----- -------- ----
+ 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
+ -------- ------- --- -------
+ 1024 11 99% 1 file
+---
+
+name: unzip -v
+command: unzip -v $FILES/example.zip d1/d2/x.txt
+after: [ ! -f d1/d2/file ]
+expected-stdout:
+ Archive: $FILES/example.zip
+ Length Method Size Cmpr Date Time CRC-32 Name
+ -------- ------ ------- ---- ---------- ----- -------- ----
+ 1024 Defl:N 11 99% 2017-06-04 08:45 48d7f063 d1/d2/x.txt
+ -------- ------- --- -------
+ 1024 11 99% 1 file
+---
+
+name: unzip one file
+command: unzip -q $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+after: [ ! -f d1/d2/b.txt ]
+expected-stdout:
+ a
+---
+
+name: unzip all files
+command: unzip -q $FILES/example.zip
+after: [ -f d1/d2/a.txt ]
+after: [ -f d1/d2/b.txt ]
+after: [ -f d1/d2/c.txt ]
+after: [ -f d1/d2/empty.txt ]
+after: [ -f d1/d2/x.txt ]
+after: [ -d d1/d2/dir ]
+expected-stdout:
+---
+
+name: unzip -o
+before: mkdir -p d1/d2
+before: echo b > d1/d2/a.txt
+command: unzip -q -o $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+expected-stdout:
+ a
+---
+
+name: unzip -n
+before: mkdir -p d1/d2
+before: echo b > d1/d2/a.txt
+command: unzip -q -n $FILES/example.zip d1/d2/a.txt && cat d1/d2/a.txt
+expected-stdout:
+ b
+---
+
+# The reference implementation will create *one* level of missing directories,
+# so this succeeds.
+name: unzip -d shallow non-existent
+command: unzip -q -d will-be-created $FILES/example.zip d1/d2/a.txt
+after: [ -d will-be-created ]
+after: [ -f will-be-created/d1/d2/a.txt ]
+---
+
+# The reference implementation will *only* create one level of missing
+# directories, so this fails.
+name: unzip -d deep non-existent
+command: unzip -q -d oh-no/will-not-be-created $FILES/example.zip d1/d2/a.txt 2> stderr ; echo $? > status
+after: [ ! -d oh-no ]
+after: [ ! -d oh-no/will-not-be-created ]
+after: [ ! -f oh-no/will-not-be-created/d1/d2/a.txt ]
+after: grep -q "oh-no/will-not-be-created" stderr
+after: grep -q "No such file or directory" stderr
+# The reference implementation has *lots* of non-zero exit values, but we stick to 0 and 1.
+after: [ $(cat status) -gt 0 ]
+---
+
+name: unzip -d exists
+before: mkdir dir
+command: unzip -q -d dir $FILES/example.zip d1/d2/a.txt && cat dir/d1/d2/a.txt
+after: [ ! -f d1/d2/a.txt ]
+expected-stdout:
+ a
+---
+
+name: unzip -p
+command: unzip -p $FILES/example.zip d1/d2/a.txt
+after: [ ! -f d1/d2/a.txt ]
+expected-stdout:
+ a
+---
+
+name: unzip -x FILE...
+# Note: the RI ignores -x DIR for some reason, but it's not obvious we should.
+command: unzip -q $FILES/example.zip -x d1/d2/a.txt d1/d2/b.txt d1/d2/empty.txt d1/d2/x.txt && cat d1/d2/c.txt
+after: [ ! -f d1/d2/a.txt ]
+after: [ ! -f d1/d2/b.txt ]
+after: [ ! -f d1/d2/empty.txt ]
+after: [ ! -f d1/d2/x.txt ]
+after: [ -d d1/d2/dir ]
+expected-stdout:
+ ccc
+---
+
+name: unzip FILE -x FILE...
+command: unzip -q $FILES/example.zip d1/d2/a.txt d1/d2/b.txt -x d1/d2/a.txt && cat d1/d2/b.txt
+after: [ ! -f d1/d2/a.txt ]
+after: [ -f d1/d2/b.txt ]
+after: [ ! -f d1/d2/c.txt ]
+after: [ ! -f d1/d2/empty.txt ]
+after: [ ! -f d1/d2/x.txt ]
+after: [ ! -d d1/d2/dir ]
+expected-stdout:
+ bb
+---
diff --git a/libziparchive/cli-tests/zipinfo.test b/libziparchive/cli-tests/zipinfo.test
new file mode 100755
index 000000000..d5bce1c01
--- /dev/null
+++ b/libziparchive/cli-tests/zipinfo.test
@@ -0,0 +1,53 @@
+# zipinfo tests.
+
+# Note: since "master key", Android uses libziparchive for all zip file
+# handling, and that scans the whole central directory immediately. Not only
+# lookups by name but also iteration is implemented using the resulting hash
+# table, meaning that any test that makes assumptions about iteration order
+# will fail on Android.
+
+name: zipinfo -1
+command: zipinfo -1 $FILES/example.zip | sort
+expected-stdout:
+ d1/
+ d1/d2/a.txt
+ d1/d2/b.txt
+ d1/d2/c.txt
+ d1/d2/dir/
+ d1/d2/empty.txt
+ d1/d2/x.txt
+---
+
+name: zipinfo header
+command: zipinfo $FILES/example.zip | head -2
+expected-stdout:
+ Archive: $FILES/example.zip
+ Zip file size: 1082 bytes, number of entries: 7
+---
+
+name: zipinfo footer
+command: zipinfo $FILES/example.zip | tail -1
+expected-stdout:
+ 7 files, 1033 bytes uncompressed, 20 bytes compressed: 98.1%
+---
+
+name: zipinfo directory
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/ | sed s/17-Jun-/2017-06-/
+expected-stdout:
+ drwxr-x--- 3.0 unx 0 bx stor 2017-06-04 08:40 d1/
+---
+
+name: zipinfo stored
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/d2/empty.txt | sed s/17-Jun-/2017-06-/
+expected-stdout:
+ -rw-r----- 3.0 unx 0 bx stor 2017-06-04 08:43 d1/d2/empty.txt
+---
+
+name: zipinfo deflated
+# The RI doesn't use ISO dates.
+command: zipinfo $FILES/example.zip d1/d2/x.txt | sed s/17-Jun-/2017-06-/
+expected-stdout:
+ -rw-r----- 3.0 unx 1024 tx defN 2017-06-04 08:45 d1/d2/x.txt
+---
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
new file mode 100644
index 000000000..10311b565
--- /dev/null
+++ b/libziparchive/entry_name_utils-inl.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
+#define LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+// Check if |length| bytes at |entry_name| constitute a valid entry name.
+// Entry names must be valid UTF-8 and must not contain '0'. They also must
+// fit into the central directory record.
+inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
+ if (length > std::numeric_limits<uint16_t>::max()) {
+ return false;
+ }
+ for (size_t i = 0; i < length; ++i) {
+ const uint8_t byte = entry_name[i];
+ if (byte == 0) {
+ return false;
+ } else if ((byte & 0x80) == 0) {
+ // Single byte sequence.
+ continue;
+ } else if ((byte & 0xc0) == 0x80 || (byte & 0xfe) == 0xfe) {
+ // Invalid sequence.
+ return false;
+ } else {
+ // 2-5 byte sequences.
+ for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
+ first = static_cast<uint8_t>((first & 0x7f) << 1)) {
+ ++i;
+
+ // Missing continuation byte..
+ if (i == length) {
+ return false;
+ }
+
+ // Invalid continuation byte.
+ const uint8_t continuation_byte = entry_name[i];
+ if ((continuation_byte & 0xc0) != 0x80) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+#endif // LIBZIPARCHIVE_ENTRY_NAME_UTILS_INL_H_
diff --git a/libziparchive/entry_name_utils_test.cc b/libziparchive/entry_name_utils_test.cc
new file mode 100644
index 000000000..d83d8540c
--- /dev/null
+++ b/libziparchive/entry_name_utils_test.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "entry_name_utils-inl.h"
+
+#include <gtest/gtest.h>
+
+TEST(entry_name_utils, NullChars) {
+ // 'A', 'R', '\0', 'S', 'E'
+ const uint8_t zeroes[] = {0x41, 0x52, 0x00, 0x53, 0x45};
+ ASSERT_FALSE(IsValidEntryName(zeroes, sizeof(zeroes)));
+
+ const uint8_t zeroes_continuation_chars[] = {0xc2, 0xa1, 0xc2, 0x00};
+ ASSERT_FALSE(IsValidEntryName(zeroes_continuation_chars, sizeof(zeroes_continuation_chars)));
+}
+
+TEST(entry_name_utils, InvalidSequence) {
+ // 0xfe is an invalid start byte
+ const uint8_t invalid[] = {0x41, 0xfe};
+ ASSERT_FALSE(IsValidEntryName(invalid, sizeof(invalid)));
+
+ // 0x91 is an invalid start byte (it's a valid continuation byte).
+ const uint8_t invalid2[] = {0x41, 0x91};
+ ASSERT_FALSE(IsValidEntryName(invalid2, sizeof(invalid2)));
+}
+
+TEST(entry_name_utils, TruncatedContinuation) {
+ // Malayalam script with truncated bytes. There should be 2 bytes
+ // after 0xe0
+ const uint8_t truncated[] = {0xe0, 0xb4, 0x85, 0xe0, 0xb4};
+ ASSERT_FALSE(IsValidEntryName(truncated, sizeof(truncated)));
+
+ // 0xc2 is the start of a 2 byte sequence that we've subsequently
+ // dropped.
+ const uint8_t truncated2[] = {0xc2, 0xc2, 0xa1};
+ ASSERT_FALSE(IsValidEntryName(truncated2, sizeof(truncated2)));
+}
+
+TEST(entry_name_utils, BadContinuation) {
+ // 0x41 is an invalid continuation char, since it's MSBs
+ // aren't "10..." (are 01).
+ const uint8_t bad[] = {0xc2, 0xa1, 0xc2, 0x41};
+ ASSERT_FALSE(IsValidEntryName(bad, sizeof(bad)));
+
+ // 0x41 is an invalid continuation char, since it's MSBs
+ // aren't "10..." (are 11).
+ const uint8_t bad2[] = {0xc2, 0xa1, 0xc2, 0xfe};
+ ASSERT_FALSE(IsValidEntryName(bad2, sizeof(bad2)));
+}
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
new file mode 100644
index 000000000..fbc47dbc4
--- /dev/null
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <string>
+#include <string_view>
+
+#include "android-base/off64_t.h"
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+/*
+ * Represents information about a zip entry in a zip file.
+ */
+struct ZipEntry {
+ // Compression method. One of kCompressStored or kCompressDeflated.
+ // See also `gpbf` for deflate subtypes.
+ uint16_t method;
+
+ // Modification time. The zipfile format specifies
+ // that the first two little endian bytes contain the time
+ // and the last two little endian bytes contain the date.
+ // See `GetModificationTime`. Use signed integer to avoid the
+ // sub-overflow.
+ // TODO: should be overridden by extra time field, if present.
+ int32_t mod_time;
+
+ // Returns `mod_time` as a broken-down struct tm.
+ struct tm GetModificationTime() const;
+
+ // Suggested Unix mode for this entry, from the zip archive if created on
+ // Unix, or a default otherwise. See also `external_file_attributes`.
+ mode_t unix_mode;
+
+ // 1 if this entry contains a data descriptor segment, 0
+ // otherwise.
+ uint8_t has_data_descriptor;
+
+ // Crc32 value of this ZipEntry. This information might
+ // either be stored in the local file header or in a special
+ // Data descriptor footer at the end of the file entry.
+ uint32_t crc32;
+
+ // Compressed length of this ZipEntry. Might be present
+ // either in the local file header or in the data descriptor
+ // footer.
+ uint32_t compressed_length;
+
+ // Uncompressed length of this ZipEntry. Might be present
+ // either in the local file header or in the data descriptor
+ // footer.
+ uint32_t uncompressed_length;
+
+ // The offset to the start of data for this ZipEntry.
+ off64_t offset;
+
+ // The version of zip and the host file system this came from (for zipinfo).
+ uint16_t version_made_by;
+
+ // The raw attributes, whose interpretation depends on the host
+ // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
+ uint32_t external_file_attributes;
+
+ // Specifics about the deflation (for zipinfo).
+ uint16_t gpbf;
+ // Whether this entry is believed to be text or binary (for zipinfo).
+ bool is_text;
+};
+
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
+
+/*
+ * Open a Zip archive, and sets handle to the value of the opaque
+ * handle for the file. This handle must be released by calling
+ * CloseArchive with this handle.
+ *
+ * Returns 0 on success, and negative values on failure.
+ */
+int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
+
+/*
+ * Like OpenArchive, but takes a file descriptor open for reading
+ * at the start of the file. The descriptor must be mappable (this does
+ * not allow access to a stream).
+ *
+ * Sets handle to the value of the opaque handle for this file descriptor.
+ * This handle must be released by calling CloseArchive with this handle.
+ *
+ * If assume_ownership parameter is 'true' calling CloseArchive will close
+ * the file.
+ *
+ * This function maps and scans the central directory and builds a table
+ * of entries for future lookups.
+ *
+ * "debugFileName" will appear in error messages, but is not otherwise used.
+ *
+ * Returns 0 on success, and negative values on failure.
+ */
+int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+ bool assume_ownership = true);
+
+int32_t OpenArchiveFdRange(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
+ off64_t length, off64_t offset, bool assume_ownership = true);
+
+int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
+ ZipArchiveHandle* handle);
+/*
+ * Close archive, releasing resources associated with it. This will
+ * unmap the central directory of the zipfile and free all internal
+ * data structures associated with the file. It is an error to use
+ * this handle for any further operations without an intervening
+ * call to one of the OpenArchive variants.
+ */
+void CloseArchive(ZipArchiveHandle archive);
+
+/** See GetArchiveInfo(). */
+struct ZipArchiveInfo {
+ /** The size in bytes of the archive itself. Used by zipinfo. */
+ off64_t archive_size;
+ /** The number of entries in the archive. */
+ size_t entry_count;
+};
+
+/**
+ * Returns information about the given archive.
+ */
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
+
+/*
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
+ *
+ * Returns 0 if an entry is found, and populates |data| with information
+ * about this entry. Returns negative values otherwise.
+ *
+ * It's important to note that |data->crc32|, |data->compLen| and
+ * |data->uncompLen| might be set to values from the central directory
+ * if this file entry contains a data descriptor footer. To verify crc32s
+ * and length, a call to VerifyCrcAndLengths must be made after entry data
+ * has been processed.
+ *
+ * On non-Windows platforms this method does not modify internal state and
+ * can be called concurrently.
+ */
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+
+/*
+ * Start iterating over all entries of a zip file. The order of iteration
+ * is not guaranteed to be the same as the order of elements
+ * in the central directory but is stable for a given zip file. |cookie| will
+ * contain the value of an opaque cookie which can be used to make one or more
+ * calls to Next. All calls to StartIteration must be matched by a call to
+ * EndIteration to free any allocated memory.
+ *
+ * This method also accepts optional prefix and suffix to restrict iteration to
+ * entry names that start with |optional_prefix| or end with |optional_suffix|.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+ const std::string_view optional_prefix = "",
+ const std::string_view optional_suffix = "");
+
+/*
+ * Advance to the next element in the zipfile in iteration order.
+ *
+ * Returns 0 on success, -1 if there are no more elements in this
+ * archive and lower negative values on failure.
+ */
+int32_t Next(void* cookie, ZipEntry* data, std::string* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
+
+/*
+ * End iteration over all entries of a zip file and frees the memory allocated
+ * in StartIteration.
+ */
+void EndIteration(void* cookie);
+
+/*
+ * Uncompress and write an entry to an open file identified by |fd|.
+ * |entry->uncompressed_length| bytes will be written to the file at
+ * its current offset, and the file will be truncated at the end of
+ * the uncompressed data (no truncation if |fd| references a block
+ * device).
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd);
+
+/**
+ * Uncompress a given zip entry to the memory region at |begin| and of
+ * size |size|. This size is expected to be the same as the *declared*
+ * uncompressed length of the zip entry. It is an error if the *actual*
+ * number of uncompressed bytes differs from this number.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size);
+
+int GetFileDescriptor(const ZipArchiveHandle archive);
+
+/**
+ * Returns the offset of the zip archive in the backing file descriptor, or 0 if the zip archive is
+ * not backed by a file descriptor.
+ */
+off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive);
+
+const char* ErrorCodeString(int32_t error_code);
+
+#if !defined(_WIN32)
+typedef bool (*ProcessZipEntryFunction)(const uint8_t* buf, size_t buf_size, void* cookie);
+
+/*
+ * Stream the uncompressed data through the supplied function,
+ * passing cookie to it each time it gets called.
+ */
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie);
+#endif
+
+namespace zip_archive {
+
+class Writer {
+ public:
+ virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+ virtual ~Writer();
+
+ protected:
+ Writer() = default;
+
+ private:
+ Writer(const Writer&) = delete;
+ void operator=(const Writer&) = delete;
+};
+
+class Reader {
+ public:
+ virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0;
+ virtual ~Reader();
+
+ protected:
+ Reader() = default;
+
+ private:
+ Reader(const Reader&) = delete;
+ void operator=(const Reader&) = delete;
+};
+
+/*
+ * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
+ * |crc_out| is set to the CRC32 checksum of the uncompressed data.
+ *
+ * Returns 0 on success and negative values on failure, for example if |reader|
+ * cannot supply the right amount of data, or if the number of bytes written to
+ * data does not match |uncompressed_length|.
+ *
+ * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
+ * uncompressed data.
+ */
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+ const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
+} // namespace zip_archive
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
new file mode 100644
index 000000000..8c6ca795a
--- /dev/null
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Read-only stream access to Zip archives entries.
+#pragma once
+
+#include <ziparchive/zip_archive.h>
+
+#include <vector>
+
+#include "android-base/off64_t.h"
+
+class ZipArchiveStreamEntry {
+ public:
+ virtual ~ZipArchiveStreamEntry() {}
+
+ virtual const std::vector<uint8_t>* Read() = 0;
+
+ virtual bool Verify() = 0;
+
+ static ZipArchiveStreamEntry* Create(ZipArchiveHandle handle, const ZipEntry& entry);
+ static ZipArchiveStreamEntry* CreateRaw(ZipArchiveHandle handle, const ZipEntry& entry);
+
+ protected:
+ ZipArchiveStreamEntry(ZipArchiveHandle handle) : handle_(handle) {}
+
+ virtual bool Init(const ZipEntry& entry);
+
+ ZipArchiveHandle handle_;
+
+ off64_t offset_ = 0;
+ uint32_t crc32_ = 0u;
+};
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
new file mode 100644
index 000000000..d68683dfe
--- /dev/null
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdio>
+#include <ctime>
+
+#include <gtest/gtest_prod.h>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+
+struct z_stream_s;
+typedef struct z_stream_s z_stream;
+
+/**
+ * Writes a Zip file via a stateful interface.
+ *
+ * Example:
+ *
+ * FILE* file = fopen("path/to/zip.zip", "wb");
+ *
+ * ZipWriter writer(file);
+ *
+ * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
+ * writer.WriteBytes(buffer, bufferLen);
+ * writer.WriteBytes(buffer2, bufferLen2);
+ * writer.FinishEntry();
+ *
+ * writer.StartEntry("empty.txt", 0);
+ * writer.FinishEntry();
+ *
+ * writer.Finish();
+ *
+ * fclose(file);
+ */
+class ZipWriter {
+ public:
+ enum {
+ /**
+ * Flag to compress the zip entry using deflate.
+ */
+ kCompress = 0x01,
+
+ /**
+ * Flag to align the zip entry data on a 32bit boundary. Useful for
+ * mmapping the data at runtime.
+ */
+ kAlign32 = 0x02,
+ };
+
+ /**
+ * A struct representing a zip file entry.
+ */
+ struct FileEntry {
+ std::string path;
+ uint16_t compression_method;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ uint16_t padding_length;
+ off64_t local_file_header_offset;
+ };
+
+ static const char* ErrorCodeString(int32_t error_code);
+
+ /**
+ * Create a ZipWriter that will write into a FILE stream. The file should be opened with
+ * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
+ * caller is responsible for closing the file.
+ */
+ explicit ZipWriter(FILE* f);
+
+ // Move constructor.
+ ZipWriter(ZipWriter&& zipWriter) noexcept;
+
+ // Move assignment.
+ ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
+
+ /**
+ * Starts a new zip entry with the given path and flags.
+ * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartEntry(std::string_view path, size_t flags);
+
+ /**
+ * Starts a new zip entry with the given path and flags, where the
+ * entry will be aligned to the given alignment.
+ * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
+ * will result in an error.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
+
+ /**
+ * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
+
+ /**
+ * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
+
+ /**
+ * Writes bytes to the zip file for the previously started zip entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t WriteBytes(const void* data, size_t len);
+
+ /**
+ * Finish a zip entry started with StartEntry(const char*, size_t) or
+ * StartEntryWithTime(const char*, size_t, time_t). This must be called before
+ * any new zip entries are started, or before Finish() is called.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t FinishEntry();
+
+ /**
+ * Discards the last-written entry. Can only be called after an entry has been written using
+ * FinishEntry().
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t DiscardLastEntry();
+
+ /**
+ * Sets `out_entry` to the last entry written after a call to FinishEntry().
+ * Returns 0 on success, and an error value < 0 if no entries have been written.
+ */
+ int32_t GetLastEntry(FileEntry* out_entry);
+
+ /**
+ * Writes the Central Directory Headers and flushes the zip file stream.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t Finish();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ZipWriter);
+
+ int32_t HandleError(int32_t error_code);
+ int32_t PrepareDeflate();
+ int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
+ int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
+ int32_t FlushCompressedBytes(FileEntry* file);
+ bool ShouldUseDataDescriptor() const;
+
+ enum class State {
+ kWritingZip,
+ kWritingEntry,
+ kDone,
+ kError,
+ };
+
+ FILE* file_;
+ bool seekable_;
+ off64_t current_offset_;
+ State state_;
+ std::vector<FileEntry> files_;
+ FileEntry current_file_entry_;
+
+ std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
+ std::vector<uint8_t> buffer_;
+
+ FRIEND_TEST(zipwriter, WriteToUnseekableFile);
+};
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
new file mode 100644
index 000000000..75e7939da
--- /dev/null
+++ b/libziparchive/libziparchive_fuzzer.cpp
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ziparchive/zip_archive.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ ZipArchiveHandle handle = nullptr;
+ OpenArchiveFromMemory(data, size, "fuzz", &handle);
+ CloseArchive(handle);
+ return 0;
+}
diff --git a/libziparchive/run-ziptool-tests-on-android.sh b/libziparchive/run-ziptool-tests-on-android.sh
new file mode 100755
index 000000000..3c23d4373
--- /dev/null
+++ b/libziparchive/run-ziptool-tests-on-android.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Copy the tests across.
+adb shell rm -rf /data/local/tmp/ziptool-tests/
+adb shell mkdir /data/local/tmp/ziptool-tests/
+adb push cli-tests/ /data/local/tmp/ziptool-tests/
+#adb push cli-test /data/local/tmp/ziptool-tests/
+
+if tty -s; then
+ dash_t="-t"
+else
+ dash_t=""
+fi
+
+exec adb shell $dash_t cli-test /data/local/tmp/ziptool-tests/cli-tests/*.test
diff --git a/libziparchive/testdata/bad_crc.zip b/libziparchive/testdata/bad_crc.zip
new file mode 100644
index 000000000..e12ba07bd
--- /dev/null
+++ b/libziparchive/testdata/bad_crc.zip
Binary files differ
diff --git a/libziparchive/testdata/bad_filename.zip b/libziparchive/testdata/bad_filename.zip
new file mode 100644
index 000000000..294eaf562
--- /dev/null
+++ b/libziparchive/testdata/bad_filename.zip
Binary files differ
diff --git a/libziparchive/testdata/crash.apk b/libziparchive/testdata/crash.apk
new file mode 100644
index 000000000..d6dd52dd7
--- /dev/null
+++ b/libziparchive/testdata/crash.apk
Binary files differ
diff --git a/libziparchive/testdata/declaredlength.zip b/libziparchive/testdata/declaredlength.zip
new file mode 100644
index 000000000..773380c45
--- /dev/null
+++ b/libziparchive/testdata/declaredlength.zip
Binary files differ
diff --git a/libziparchive/testdata/dummy-update.zip b/libziparchive/testdata/dummy-update.zip
new file mode 100644
index 000000000..6976bf155
--- /dev/null
+++ b/libziparchive/testdata/dummy-update.zip
Binary files differ
diff --git a/libziparchive/testdata/empty.zip b/libziparchive/testdata/empty.zip
new file mode 100644
index 000000000..15cb0ecb3
--- /dev/null
+++ b/libziparchive/testdata/empty.zip
Binary files differ
diff --git a/libziparchive/testdata/large.zip b/libziparchive/testdata/large.zip
new file mode 100644
index 000000000..49659c832
--- /dev/null
+++ b/libziparchive/testdata/large.zip
Binary files differ
diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip
new file mode 100644
index 000000000..9e7cb7800
--- /dev/null
+++ b/libziparchive/testdata/valid.zip
Binary files differ
diff --git a/libziparchive/testdata/zero-size-cd.zip b/libziparchive/testdata/zero-size-cd.zip
new file mode 100644
index 000000000..b6c8cbeac
--- /dev/null
+++ b/libziparchive/testdata/zero-size-cd.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
new file mode 100644
index 000000000..489fcb1aa
--- /dev/null
+++ b/libziparchive/zip_archive.cc
@@ -0,0 +1,1315 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+
+#define LOG_TAG "ziparchive"
+
+#include "ziparchive/zip_archive.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#if defined(__APPLE__)
+#define lseek64 lseek
+#endif
+
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
+#include <android-base/mapped_file.h>
+#include <android-base/memory.h>
+#include <android-base/strings.h>
+#include <android-base/utf8.h>
+#include <log/log.h>
+#include "zlib.h"
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+#include "zip_archive_private.h"
+
+using android::base::get_unaligned;
+
+// Used to turn on crc checks - verify that the content CRC matches the values
+// specified in the local file header and the central directory.
+static constexpr bool kCrcChecksEnabled = false;
+
+// The maximum number of bytes to scan backwards for the EOCD start.
+static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
+
+/*
+ * A Read-only Zip archive.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and
+ * we want to use as little memory as possible. We memory-map the zip
+ * central directory, and load a hash table with pointers to the filenames
+ * (which aren't null-terminated). The other fields are at a fixed offset
+ * from the filename, so we don't need to extract those (but we do need
+ * to byte-read and endian-swap them every time we want them).
+ *
+ * It's possible that somebody has handed us a massive (~1GB) zip archive,
+ * so we can't expect to mmap the entire file.
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure. However, this requires a private mapping of
+ * every page that the Central Directory touches. Easier to tuck a copy
+ * of the string length into the hash table entry.
+ */
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+static uint32_t RoundUpPower2(uint32_t val) {
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
+
+static uint32_t ComputeHash(std::string_view name) {
+ return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
+}
+
+/*
+ * Convert a ZipEntry to a hash table index, verifying that it's in a
+ * valid range.
+ */
+static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
+ std::string_view name, const uint8_t* start) {
+ const uint32_t hash = ComputeHash(name);
+
+ // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
+ uint32_t ent = hash & (hash_table_size - 1);
+ while (hash_table[ent].name_offset != 0) {
+ if (hash_table[ent].ToStringView(start) == name) {
+ return ent;
+ }
+ ent = (ent + 1) & (hash_table_size - 1);
+ }
+
+ ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
+ return kEntryNotFound;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size,
+ std::string_view name, const uint8_t* start) {
+ const uint64_t hash = ComputeHash(name);
+ uint32_t ent = hash & (hash_table_size - 1);
+
+ /*
+ * We over-allocated the table, so we're guaranteed to find an empty slot.
+ * Further, we guarantee that the hashtable size is not 0.
+ */
+ while (hash_table[ent].name_offset != 0) {
+ if (hash_table[ent].ToStringView(start) == name) {
+ // We've found a duplicate entry. We don't accept duplicates.
+ ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
+ return kDuplicateEntry;
+ }
+ ent = (ent + 1) & (hash_table_size - 1);
+ }
+
+ // `name` has already been validated before entry.
+ const char* start_char = reinterpret_cast<const char*>(start);
+ hash_table[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
+ hash_table[ent].name_length = static_cast<uint16_t>(name.size());
+ return 0;
+}
+
+#if defined(__BIONIC__)
+uint64_t GetOwnerTag(const ZipArchive* archive) {
+ return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
+ reinterpret_cast<uint64_t>(archive));
+}
+#endif
+
+ZipArchive::ZipArchive(MappedZipFile&& map, bool assume_ownership)
+ : mapped_zip(map),
+ close_file(assume_ownership),
+ directory_offset(0),
+ central_directory(),
+ directory_map(),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(nullptr) {
+#if defined(__BIONIC__)
+ if (assume_ownership) {
+ CHECK(mapped_zip.HasFd());
+ android_fdsan_exchange_owner_tag(mapped_zip.GetFileDescriptor(), 0, GetOwnerTag(this));
+ }
+#endif
+}
+
+ZipArchive::ZipArchive(const void* address, size_t length)
+ : mapped_zip(address, length),
+ close_file(false),
+ directory_offset(0),
+ central_directory(),
+ directory_map(),
+ num_entries(0),
+ hash_table_size(0),
+ hash_table(nullptr) {}
+
+ZipArchive::~ZipArchive() {
+ if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
+#if defined(__BIONIC__)
+ android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
+#else
+ close(mapped_zip.GetFileDescriptor());
+#endif
+ }
+
+ free(hash_table);
+}
+
+static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
+ off64_t file_length, uint32_t read_amount,
+ uint8_t* scan_buffer) {
+ const off64_t search_start = file_length - read_amount;
+
+ if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
+ ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed", static_cast<int64_t>(read_amount),
+ static_cast<int64_t>(search_start));
+ return kIoError;
+ }
+
+ /*
+ * Scan backward for the EOCD magic. In an archive without a trailing
+ * comment, we'll find it on the first try. (We may want to consider
+ * doing an initial minimal read; if we don't find it, retry with a
+ * second read as above.)
+ */
+ CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
+ int32_t i = read_amount - sizeof(EocdRecord);
+ for (; i >= 0; i--) {
+ if (scan_buffer[i] == 0x50) {
+ uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
+ if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
+ ALOGV("+++ Found EOCD at buf+%d", i);
+ break;
+ }
+ }
+ }
+ if (i < 0) {
+ ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
+ return kInvalidFile;
+ }
+
+ const off64_t eocd_offset = search_start + i;
+ const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
+ /*
+ * Verify that there's no trailing space at the end of the central directory
+ * and its comment.
+ */
+ const off64_t calculated_length = eocd_offset + sizeof(EocdRecord) + eocd->comment_length;
+ if (calculated_length != file_length) {
+ ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
+ static_cast<int64_t>(file_length - calculated_length));
+ return kInvalidFile;
+ }
+
+ /*
+ * Grab the CD offset and size, and the number of entries in the
+ * archive and verify that they look reasonable.
+ */
+ if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
+ ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
+ eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
+ return kInvalidOffset;
+ }
+ if (eocd->num_records == 0) {
+#if defined(__ANDROID__)
+ ALOGW("Zip: empty archive?");
+#endif
+ return kEmptyArchive;
+ }
+
+ ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32, eocd->num_records,
+ eocd->cd_size, eocd->cd_start_offset);
+
+ // It all looks good. Create a mapping for the CD, and set the fields
+ // in archive.
+ if (!archive->InitializeCentralDirectory(static_cast<off64_t>(eocd->cd_start_offset),
+ static_cast<size_t>(eocd->cd_size))) {
+ return kMmapFailed;
+ }
+
+ archive->num_entries = eocd->num_records;
+ archive->directory_offset = eocd->cd_start_offset;
+
+ return 0;
+}
+
+/*
+ * Find the zip Central Directory and memory-map it.
+ *
+ * On success, returns 0 after populating fields from the EOCD area:
+ * directory_offset
+ * directory_ptr
+ * num_entries
+ */
+static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
+ // Test file length. We use lseek64 to make sure the file
+ // is small enough to be a zip file (Its size must be less than
+ // 0xffffffff bytes).
+ off64_t file_length = archive->mapped_zip.GetFileLength();
+ if (file_length == -1) {
+ return kInvalidFile;
+ }
+
+ if (file_length > static_cast<off64_t>(0xffffffff)) {
+ ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
+ return kInvalidFile;
+ }
+
+ if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
+ ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
+ return kInvalidFile;
+ }
+
+ /*
+ * Perform the traditional EOCD snipe hunt.
+ *
+ * We're searching for the End of Central Directory magic number,
+ * which appears at the start of the EOCD block. It's followed by
+ * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
+ * need to read the last part of the file into a buffer, dig through
+ * it to find the magic number, parse some values out, and use those
+ * to determine the extent of the CD.
+ *
+ * We start by pulling in the last part of the file.
+ */
+ uint32_t read_amount = kMaxEOCDSearch;
+ if (file_length < read_amount) {
+ read_amount = static_cast<uint32_t>(file_length);
+ }
+
+ std::vector<uint8_t> scan_buffer(read_amount);
+ int32_t result =
+ MapCentralDirectory0(debug_file_name, archive, file_length, read_amount, scan_buffer.data());
+ return result;
+}
+
+/*
+ * Parses the Zip archive's Central Directory. Allocates and populates the
+ * hash table.
+ *
+ * Returns 0 on success.
+ */
+static int32_t ParseZipArchive(ZipArchive* archive) {
+ const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
+ const size_t cd_length = archive->central_directory.GetMapLength();
+ const uint16_t num_entries = archive->num_entries;
+
+ /*
+ * Create hash table. We have a minimum 75% load factor, possibly as
+ * low as 50% after we round off to a power of 2. There must be at
+ * least one unused entry to avoid an infinite loop during creation.
+ */
+ archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
+ archive->hash_table =
+ reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
+ if (archive->hash_table == nullptr) {
+ ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
+ archive->hash_table_size, sizeof(ZipStringOffset));
+ return kAllocationFailed;
+ }
+
+ /*
+ * Walk through the central directory, adding entries to the hash
+ * table and verifying values.
+ */
+ const uint8_t* const cd_end = cd_ptr + cd_length;
+ const uint8_t* ptr = cd_ptr;
+ for (uint16_t i = 0; i < num_entries; i++) {
+ if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
+ ALOGW("Zip: ran off the end (item #%" PRIu16 ", %zu bytes of central directory)", i,
+ cd_length);
+#if defined(__ANDROID__)
+ android_errorWriteLog(0x534e4554, "36392138");
+#endif
+ return kInvalidFile;
+ }
+
+ const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+ if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
+ ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
+ return kInvalidFile;
+ }
+
+ const off64_t local_header_offset = cdr->local_file_header_offset;
+ if (local_header_offset >= archive->directory_offset) {
+ ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
+ static_cast<int64_t>(local_header_offset), i);
+ return kInvalidFile;
+ }
+
+ const uint16_t file_name_length = cdr->file_name_length;
+ const uint16_t extra_length = cdr->extra_field_length;
+ const uint16_t comment_length = cdr->comment_length;
+ const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
+
+ if (file_name + file_name_length > cd_end) {
+ ALOGW("Zip: file name for entry %" PRIu16
+ " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
+ i, file_name_length, cd_length);
+ return kInvalidEntryName;
+ }
+ // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
+ if (!IsValidEntryName(file_name, file_name_length)) {
+ ALOGW("Zip: invalid file name at entry %" PRIu16, i);
+ return kInvalidEntryName;
+ }
+
+ // Add the CDE filename to the hash table.
+ std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
+ const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
+ archive->central_directory.GetBasePtr());
+ if (add_result != 0) {
+ ALOGW("Zip: Error adding entry to hash table %d", add_result);
+ return add_result;
+ }
+
+ ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
+ if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
+ ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16, ptr - cd_ptr, cd_length, i);
+ return kInvalidFile;
+ }
+ }
+
+ uint32_t lfh_start_bytes;
+ if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
+ sizeof(uint32_t), 0)) {
+ ALOGW("Zip: Unable to read header for entry at offset == 0.");
+ return kInvalidFile;
+ }
+
+ if (lfh_start_bytes != LocalFileHeader::kSignature) {
+ ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
+#if defined(__ANDROID__)
+ android_errorWriteLog(0x534e4554, "64211847");
+#endif
+ return kInvalidFile;
+ }
+
+ ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
+
+ return 0;
+}
+
+static int32_t OpenArchiveInternal(ZipArchive* archive, const char* debug_file_name) {
+ int32_t result = MapCentralDirectory(debug_file_name, archive);
+ return result != 0 ? result : ParseZipArchive(archive);
+}
+
+int32_t OpenArchiveFd(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+ bool assume_ownership) {
+ ZipArchive* archive = new ZipArchive(MappedZipFile(fd), assume_ownership);
+ *handle = archive;
+ return OpenArchiveInternal(archive, debug_file_name);
+}
+
+int32_t OpenArchiveFdRange(int fd, const char* debug_file_name, ZipArchiveHandle* handle,
+ off64_t length, off64_t offset, bool assume_ownership) {
+ ZipArchive* archive = new ZipArchive(MappedZipFile(fd, length, offset), assume_ownership);
+ *handle = archive;
+
+ if (length < 0) {
+ ALOGW("Invalid zip length %" PRId64, length);
+ return kIoError;
+ }
+
+ if (offset < 0) {
+ ALOGW("Invalid zip offset %" PRId64, offset);
+ return kIoError;
+ }
+
+ return OpenArchiveInternal(archive, debug_file_name);
+}
+
+int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
+ const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ ZipArchive* archive = new ZipArchive(MappedZipFile(fd), true);
+ *handle = archive;
+
+ if (fd < 0) {
+ ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
+ return kIoError;
+ }
+
+ return OpenArchiveInternal(archive, fileName);
+}
+
+int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
+ ZipArchiveHandle* handle) {
+ ZipArchive* archive = new ZipArchive(address, length);
+ *handle = archive;
+ return OpenArchiveInternal(archive, debug_file_name);
+}
+
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
+ ZipArchiveInfo result;
+ result.archive_size = archive->mapped_zip.GetFileLength();
+ result.entry_count = archive->num_entries;
+ return result;
+}
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ */
+void CloseArchive(ZipArchiveHandle archive) {
+ ALOGV("Closing archive %p", archive);
+ delete archive;
+}
+
+static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
+ uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
+ off64_t offset = entry->offset;
+ if (entry->method != kCompressStored) {
+ offset += entry->compressed_length;
+ } else {
+ offset += entry->uncompressed_length;
+ }
+
+ if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
+ return kIoError;
+ }
+
+ const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
+ const uint16_t ddOffset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
+ const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + ddOffset);
+
+ // Validate that the values in the data descriptor match those in the central
+ // directory.
+ if (entry->compressed_length != descriptor->compressed_size ||
+ entry->uncompressed_length != descriptor->uncompressed_size ||
+ entry->crc32 != descriptor->crc32) {
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ entry->compressed_length, entry->uncompressed_length, entry->crc32,
+ descriptor->compressed_size, descriptor->uncompressed_size, descriptor->crc32);
+ return kInconsistentInformation;
+ }
+
+ return 0;
+}
+
+static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) {
+ const uint16_t nameLen = archive->hash_table[ent].name_length;
+
+ // Recover the start of the central directory entry from the filename
+ // pointer. The filename is the first entry past the fixed-size data,
+ // so we can just subtract back from that.
+ const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
+ const uint8_t* ptr = base_ptr + archive->hash_table[ent].name_offset;
+ ptr -= sizeof(CentralDirectoryRecord);
+
+ // This is the base of our mmapped region, we have to sanity check that
+ // the name that's in the hash table is a pointer to a location within
+ // this mapped region.
+ if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
+ ALOGW("Zip: Invalid entry pointer");
+ return kInvalidOffset;
+ }
+
+ const CentralDirectoryRecord* cdr = reinterpret_cast<const CentralDirectoryRecord*>(ptr);
+
+ // The offset of the start of the central directory in the zipfile.
+ // We keep this lying around so that we can sanity check all our lengths
+ // and our per-file structures.
+ const off64_t cd_offset = archive->directory_offset;
+
+ // Fill out the compression method, modification time, crc32
+ // and other interesting attributes from the central directory. These
+ // will later be compared against values from the local file header.
+ data->method = cdr->compression_method;
+ data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
+ data->crc32 = cdr->crc32;
+ data->compressed_length = cdr->compressed_size;
+ data->uncompressed_length = cdr->uncompressed_size;
+
+ // Figure out the local header offset from the central directory. The
+ // actual file data will begin after the local header and the name /
+ // extra comments.
+ const off64_t local_header_offset = cdr->local_file_header_offset;
+ if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
+ ALOGW("Zip: bad local hdr offset in zip");
+ return kInvalidOffset;
+ }
+
+ uint8_t lfh_buf[sizeof(LocalFileHeader)];
+ if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64,
+ static_cast<int64_t>(local_header_offset));
+ return kIoError;
+ }
+
+ const LocalFileHeader* lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
+
+ if (lfh->lfh_signature != LocalFileHeader::kSignature) {
+ ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
+ static_cast<int64_t>(local_header_offset));
+ return kInvalidOffset;
+ }
+
+ // Paranoia: Match the values specified in the local file header
+ // to those specified in the central directory.
+
+ // Warn if central directory and local file header don't agree on the use
+ // of a trailing Data Descriptor. The reference implementation is inconsistent
+ // and appears to use the LFH value during extraction (unzip) but the CD value
+ // while displayng information about archives (zipinfo). The spec remains
+ // silent on this inconsistency as well.
+ //
+ // For now, always use the version from the LFH but make sure that the values
+ // specified in the central directory match those in the data descriptor.
+ //
+ // NOTE: It's also worth noting that unzip *does* warn about inconsistencies in
+ // bit 11 (EFS: The language encoding flag, marking that filename and comment are
+ // encoded using UTF-8). This implementation does not check for the presence of
+ // that flag and always enforces that entry names are valid UTF-8.
+ if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
+ ALOGW("Zip: gpb flag mismatch at bit 3. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
+ cdr->gpb_flags, lfh->gpb_flags);
+ }
+
+ // If there is no trailing data descriptor, verify that the central directory and local file
+ // header agree on the crc, compressed, and uncompressed sizes of the entry.
+ if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
+ data->has_data_descriptor = 0;
+ if (data->compressed_length != lfh->compressed_size ||
+ data->uncompressed_length != lfh->uncompressed_size || data->crc32 != lfh->crc32) {
+ ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32 ", %" PRIx32
+ "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
+ data->compressed_length, data->uncompressed_length, data->crc32, lfh->compressed_size,
+ lfh->uncompressed_size, lfh->crc32);
+ return kInconsistentInformation;
+ }
+ } else {
+ data->has_data_descriptor = 1;
+ }
+
+ // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
+ data->version_made_by = cdr->version_made_by;
+ data->external_file_attributes = cdr->external_file_attributes;
+ if ((data->version_made_by >> 8) == 3) {
+ data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
+ } else {
+ data->unix_mode = 0777;
+ }
+
+ // 4.4.4: general purpose bit flags.
+ data->gpbf = lfh->gpb_flags;
+
+ // 4.4.14: the lowest bit of the internal file attributes field indicates text.
+ // Currently only needed to implement zipinfo.
+ data->is_text = (cdr->internal_file_attributes & 1);
+
+ // Check that the local file header name matches the declared
+ // name in the central directory.
+ if (lfh->file_name_length != nameLen) {
+ ALOGW("Zip: lfh name length did not match central directory");
+ return kInconsistentInformation;
+ }
+ const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
+ if (name_offset + lfh->file_name_length > cd_offset) {
+ ALOGW("Zip: lfh name has invalid declared length");
+ return kInvalidOffset;
+ }
+ std::vector<uint8_t> name_buf(nameLen);
+ if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
+ return kIoError;
+ }
+ const std::string_view entry_name =
+ archive->hash_table[ent].ToStringView(archive->central_directory.GetBasePtr());
+ if (memcmp(entry_name.data(), name_buf.data(), nameLen) != 0) {
+ ALOGW("Zip: lfh name did not match central directory");
+ return kInconsistentInformation;
+ }
+
+ const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader) +
+ lfh->file_name_length + lfh->extra_field_length;
+ if (data_offset > cd_offset) {
+ ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
+ return kInvalidOffset;
+ }
+
+ if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
+ ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ static_cast<int64_t>(data_offset), data->compressed_length,
+ static_cast<int64_t>(cd_offset));
+ return kInvalidOffset;
+ }
+
+ if (data->method == kCompressStored &&
+ static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
+ ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
+ static_cast<int64_t>(data_offset), data->uncompressed_length,
+ static_cast<int64_t>(cd_offset));
+ return kInvalidOffset;
+ }
+
+ data->offset = data_offset;
+ return 0;
+}
+
+struct IterationHandle {
+ ZipArchive* archive;
+
+ std::string prefix;
+ std::string suffix;
+
+ uint32_t position = 0;
+
+ IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix)
+ : archive(archive), prefix(in_prefix), suffix(in_suffix) {}
+};
+
+int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
+ const std::string_view optional_prefix,
+ const std::string_view optional_suffix) {
+ if (archive == NULL || archive->hash_table == NULL) {
+ ALOGW("Zip: Invalid ZipArchiveHandle");
+ return kInvalidHandle;
+ }
+
+ if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
+ optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: prefix/suffix too long");
+ return kInvalidEntryName;
+ }
+
+ *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix);
+ return 0;
+}
+
+void EndIteration(void* cookie) {
+ delete reinterpret_cast<IterationHandle*>(cookie);
+}
+
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry* data) {
+ if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: Invalid filename of length %zu", entryName.size());
+ return kInvalidEntryName;
+ }
+
+ const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
+ archive->central_directory.GetBasePtr());
+ if (ent < 0) {
+ ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+ return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate.
+ }
+ // We know there are at most hash_table_size entries, safe to truncate.
+ return FindEntry(archive, static_cast<uint32_t>(ent), data);
+}
+
+int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
+ std::string_view sv;
+ int32_t result = Next(cookie, data, &sv);
+ if (result == 0 && name) {
+ *name = std::string(sv);
+ }
+ return result;
+}
+
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
+ IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
+ if (handle == NULL) {
+ ALOGW("Zip: Null ZipArchiveHandle");
+ return kInvalidHandle;
+ }
+
+ ZipArchive* archive = handle->archive;
+ if (archive == NULL || archive->hash_table == NULL) {
+ ALOGW("Zip: Invalid ZipArchiveHandle");
+ return kInvalidHandle;
+ }
+
+ const uint32_t currentOffset = handle->position;
+ const uint32_t hash_table_length = archive->hash_table_size;
+ const ZipStringOffset* hash_table = archive->hash_table;
+ for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
+ const std::string_view entry_name =
+ hash_table[i].ToStringView(archive->central_directory.GetBasePtr());
+ if (hash_table[i].name_offset != 0 && (android::base::StartsWith(entry_name, handle->prefix) &&
+ android::base::EndsWith(entry_name, handle->suffix))) {
+ handle->position = (i + 1);
+ const int error = FindEntry(archive, i, data);
+ if (!error && name) {
+ *name = entry_name;
+ }
+ return error;
+ }
+ }
+
+ handle->position = 0;
+ return kIterationEnd;
+}
+
+// A Writer that writes data to a fixed size memory region.
+// The size of the memory region must be equal to the total size of
+// the data appended to it.
+class MemoryWriter : public zip_archive::Writer {
+ public:
+ MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ if (bytes_written_ + buf_size > size_) {
+ ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", size_,
+ bytes_written_ + buf_size);
+ return false;
+ }
+
+ memcpy(buf_ + bytes_written_, buf, buf_size);
+ bytes_written_ += buf_size;
+ return true;
+ }
+
+ private:
+ uint8_t* const buf_;
+ const size_t size_;
+ size_t bytes_written_;
+};
+
+// A Writer that appends data to a file |fd| at its current position.
+// The file will be truncated to the end of the written data.
+class FileWriter : public zip_archive::Writer {
+ public:
+ // Creates a FileWriter for |fd| and prepare to write |entry| to it,
+ // guaranteeing that the file descriptor is valid and that there's enough
+ // space on the volume to write out the entry completely and that the file
+ // is truncated to the correct length (no truncation if |fd| references a
+ // block device).
+ //
+ // Returns a valid FileWriter on success, |nullptr| if an error occurred.
+ static FileWriter Create(int fd, const ZipEntry* entry) {
+ const uint32_t declared_length = entry->uncompressed_length;
+ const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
+ if (current_offset == -1) {
+ ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
+ return FileWriter{};
+ }
+
+#if defined(__linux__)
+ if (declared_length > 0) {
+ // Make sure we have enough space on the volume to extract the compressed
+ // entry. Note that the call to ftruncate below will change the file size but
+ // will not allocate space on disk and this call to fallocate will not
+ // change the file size.
+ // Note: fallocate is only supported by the following filesystems -
+ // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
+ // EOPNOTSUPP error when issued in other filesystems.
+ // Hence, check for the return error code before concluding that the
+ // disk does not have enough space.
+ long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+ if (result == -1 && errno == ENOSPC) {
+ ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
+ strerror(errno));
+ return FileWriter{};
+ }
+ }
+#endif // __linux__
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ ALOGW("Zip: unable to fstat file: %s", strerror(errno));
+ return FileWriter{};
+ }
+
+ // Block device doesn't support ftruncate(2).
+ if (!S_ISBLK(sb.st_mode)) {
+ long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+ if (result == -1) {
+ ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+ static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+ return FileWriter{};
+ }
+ }
+
+ return FileWriter(fd, declared_length);
+ }
+
+ FileWriter(FileWriter&& other) noexcept
+ : fd_(other.fd_),
+ declared_length_(other.declared_length_),
+ total_bytes_written_(other.total_bytes_written_) {
+ other.fd_ = -1;
+ }
+
+ bool IsValid() const { return fd_ != -1; }
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ if (total_bytes_written_ + buf_size > declared_length_) {
+ ALOGW("Zip: Unexpected size %zu (declared) vs %zu (actual)", declared_length_,
+ total_bytes_written_ + buf_size);
+ return false;
+ }
+
+ const bool result = android::base::WriteFully(fd_, buf, buf_size);
+ if (result) {
+ total_bytes_written_ += buf_size;
+ } else {
+ ALOGW("Zip: unable to write %zu bytes to file; %s", buf_size, strerror(errno));
+ }
+
+ return result;
+ }
+
+ private:
+ explicit FileWriter(const int fd = -1, const size_t declared_length = 0)
+ : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
+
+ int fd_;
+ const size_t declared_length_;
+ size_t total_bytes_written_;
+};
+
+class EntryReader : public zip_archive::Reader {
+ public:
+ EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry)
+ : Reader(), zip_file_(zip_file), entry_(entry) {}
+
+ virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
+ }
+
+ virtual ~EntryReader() {}
+
+ private:
+ const MappedZipFile& zip_file_;
+ const ZipEntry* entry_;
+};
+
+// This method is using libz macros with old-style-casts
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
+ return inflateInit2(stream, window_bits);
+}
+#pragma GCC diagnostic pop
+
+namespace zip_archive {
+
+// Moved out of line to avoid -Wweak-vtables.
+Reader::~Reader() {}
+Writer::~Writer() {}
+
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+ const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
+ const size_t kBufSize = 32768;
+ std::vector<uint8_t> read_buf(kBufSize);
+ std::vector<uint8_t> write_buf(kBufSize);
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = &write_buf[0];
+ zstream.avail_out = kBufSize;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+ } else {
+ ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
+ }
+
+ return kZlibError;
+ }
+
+ auto zstream_deleter = [](z_stream* stream) {
+ inflateEnd(stream); /* free up any allocated structures */
+ };
+
+ std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
+
+ const bool compute_crc = (crc_out != nullptr);
+ uLong crc = 0;
+ uint32_t remaining_bytes = compressed_length;
+ do {
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+ const uint32_t offset = (compressed_length - remaining_bytes);
+ // Make sure to read at offset to ensure concurrent access to the fd.
+ if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
+ ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
+ return kIoError;
+ }
+
+ remaining_bytes -= read_size;
+
+ zstream.next_in = &read_buf[0];
+ zstream.avail_in = read_size;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, zstream.next_in,
+ zstream.avail_in, zstream.next_out, zstream.avail_out);
+ return kZlibError;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+ const size_t write_size = zstream.next_out - &write_buf[0];
+ if (!writer->Append(&write_buf[0], write_size)) {
+ return kIoError;
+ } else if (compute_crc) {
+ DCHECK_LE(write_size, kBufSize);
+ crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
+ }
+
+ zstream.next_out = &write_buf[0];
+ zstream.avail_out = kBufSize;
+ }
+ } while (zerr == Z_OK);
+
+ CHECK_EQ(zerr, Z_STREAM_END); /* other errors should've been caught */
+
+ // NOTE: zstream.adler is always set to 0, because we're using the -MAX_WBITS
+ // "feature" of zlib to tell it there won't be a zlib file header. zlib
+ // doesn't bother calculating the checksum in that scenario. We just do
+ // it ourselves above because there are no additional gains to be made by
+ // having zlib calculate it for us, since they do it by calling crc32 in
+ // the same manner that we have above.
+ if (compute_crc) {
+ *crc_out = crc;
+ }
+
+ if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
+ ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
+ uncompressed_length);
+ return kInconsistentInformation;
+ }
+
+ return 0;
+}
+} // namespace zip_archive
+
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+ zip_archive::Writer* writer, uint64_t* crc_out) {
+ const EntryReader reader(mapped_zip, entry);
+
+ return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
+ crc_out);
+}
+
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+ zip_archive::Writer* writer, uint64_t* crc_out) {
+ static const uint32_t kBufSize = 32768;
+ std::vector<uint8_t> buf(kBufSize);
+
+ const uint32_t length = entry->uncompressed_length;
+ uint32_t count = 0;
+ uLong crc = 0;
+ while (count < length) {
+ uint32_t remaining = length - count;
+ off64_t offset = entry->offset + count;
+
+ // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
+ const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+
+ // Make sure to read at offset to ensure concurrent access to the fd.
+ if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
+ ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
+ block_size, static_cast<int64_t>(offset), strerror(errno));
+ return kIoError;
+ }
+
+ if (!writer->Append(&buf[0], block_size)) {
+ return kIoError;
+ }
+ if (crc_out) {
+ crc = crc32(crc, &buf[0], block_size);
+ }
+ count += block_size;
+ }
+
+ if (crc_out) {
+ *crc_out = crc;
+ }
+
+ return 0;
+}
+
+int32_t ExtractToWriter(ZipArchiveHandle archive, ZipEntry* entry, zip_archive::Writer* writer) {
+ const uint16_t method = entry->method;
+
+ // this should default to kUnknownCompressionMethod.
+ int32_t return_value = -1;
+ uint64_t crc = 0;
+ if (method == kCompressStored) {
+ return_value =
+ CopyEntryToWriter(archive->mapped_zip, entry, writer, kCrcChecksEnabled ? &crc : nullptr);
+ } else if (method == kCompressDeflated) {
+ return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer,
+ kCrcChecksEnabled ? &crc : nullptr);
+ }
+
+ if (!return_value && entry->has_data_descriptor) {
+ return_value = ValidateDataDescriptor(archive->mapped_zip, entry);
+ if (return_value) {
+ return return_value;
+ }
+ }
+
+ // Validate that the CRC matches the calculated value.
+ if (kCrcChecksEnabled && (entry->crc32 != static_cast<uint32_t>(crc))) {
+ ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
+ return kInconsistentInformation;
+ }
+
+ return return_value;
+}
+
+int32_t ExtractToMemory(ZipArchiveHandle archive, ZipEntry* entry, uint8_t* begin, uint32_t size) {
+ MemoryWriter writer(begin, size);
+ return ExtractToWriter(archive, entry, &writer);
+}
+
+int32_t ExtractEntryToFile(ZipArchiveHandle archive, ZipEntry* entry, int fd) {
+ auto writer = FileWriter::Create(fd, entry);
+ if (!writer.IsValid()) {
+ return kIoError;
+ }
+
+ return ExtractToWriter(archive, entry, &writer);
+}
+
+const char* ErrorCodeString(int32_t error_code) {
+ // Make sure that the number of entries in kErrorMessages and ErrorCodes
+ // match.
+ static_assert((-kLastErrorCode + 1) == arraysize(kErrorMessages),
+ "(-kLastErrorCode + 1) != arraysize(kErrorMessages)");
+
+ const uint32_t idx = -error_code;
+ if (idx < arraysize(kErrorMessages)) {
+ return kErrorMessages[idx];
+ }
+
+ return "Unknown return code";
+}
+
+int GetFileDescriptor(const ZipArchiveHandle archive) {
+ return archive->mapped_zip.GetFileDescriptor();
+}
+
+off64_t GetFileDescriptorOffset(const ZipArchiveHandle archive) {
+ return archive->mapped_zip.GetFileOffset();
+}
+
+#if !defined(_WIN32)
+class ProcessWriter : public zip_archive::Writer {
+ public:
+ ProcessWriter(ProcessZipEntryFunction func, void* cookie)
+ : Writer(), proc_function_(func), cookie_(cookie) {}
+
+ virtual bool Append(uint8_t* buf, size_t buf_size) override {
+ return proc_function_(buf, buf_size, cookie_);
+ }
+
+ private:
+ ProcessZipEntryFunction proc_function_;
+ void* cookie_;
+};
+
+int32_t ProcessZipEntryContents(ZipArchiveHandle archive, ZipEntry* entry,
+ ProcessZipEntryFunction func, void* cookie) {
+ ProcessWriter writer(func, cookie);
+ return ExtractToWriter(archive, entry, &writer);
+}
+
+#endif //! defined(_WIN32)
+
+int MappedZipFile::GetFileDescriptor() const {
+ if (!has_fd_) {
+ ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
+ return -1;
+ }
+ return fd_;
+}
+
+const void* MappedZipFile::GetBasePtr() const {
+ if (has_fd_) {
+ ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
+ return nullptr;
+ }
+ return base_ptr_;
+}
+
+off64_t MappedZipFile::GetFileOffset() const {
+ return fd_offset_;
+}
+
+off64_t MappedZipFile::GetFileLength() const {
+ if (has_fd_) {
+ if (data_length_ != -1) {
+ return data_length_;
+ }
+ data_length_ = lseek64(fd_, 0, SEEK_END);
+ if (data_length_ == -1) {
+ ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
+ }
+ return data_length_;
+ } else {
+ if (base_ptr_ == nullptr) {
+ ALOGE("Zip: invalid file map");
+ return -1;
+ }
+ return data_length_;
+ }
+}
+
+// Attempts to read |len| bytes into |buf| at offset |off|.
+bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
+ if (has_fd_) {
+ if (off < 0) {
+ ALOGE("Zip: invalid offset %" PRId64, off);
+ return false;
+ }
+
+ off64_t read_offset;
+ if (__builtin_add_overflow(fd_offset_, off, &read_offset)) {
+ ALOGE("Zip: invalid read offset %" PRId64 " overflows, fd offset %" PRId64, off, fd_offset_);
+ return false;
+ }
+
+ if (data_length_ != -1) {
+ off64_t read_end;
+ if (len > std::numeric_limits<off64_t>::max() ||
+ __builtin_add_overflow(off, static_cast<off64_t>(len), &read_end)) {
+ ALOGE("Zip: invalid read length %" PRId64 " overflows, offset %" PRId64,
+ static_cast<off64_t>(len), off);
+ return false;
+ }
+
+ if (read_end > data_length_) {
+ ALOGE("Zip: invalid read length %" PRId64 " exceeds data length %" PRId64 ", offset %"
+ PRId64, static_cast<off64_t>(len), data_length_, off);
+ return false;
+ }
+ }
+
+ if (!android::base::ReadFullyAtOffset(fd_, buf, len, read_offset)) {
+ ALOGE("Zip: failed to read at offset %" PRId64, off);
+ return false;
+ }
+ } else {
+ if (off < 0 || off > data_length_) {
+ ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
+ return false;
+ }
+ memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
+ }
+ return true;
+}
+
+void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
+ size_t cd_size) {
+ base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
+ length_ = cd_size;
+}
+
+bool ZipArchive::InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size) {
+ if (mapped_zip.HasFd()) {
+ directory_map = android::base::MappedFile::FromFd(mapped_zip.GetFileDescriptor(),
+ mapped_zip.GetFileOffset() + cd_start_offset,
+ cd_size, PROT_READ);
+ if (!directory_map) {
+ ALOGE("Zip: failed to map central directory (offset %" PRId64 ", size %zu): %s",
+ cd_start_offset, cd_size, strerror(errno));
+ return false;
+ }
+
+ CHECK_EQ(directory_map->size(), cd_size);
+ central_directory.Initialize(directory_map->data(), 0 /*offset*/, cd_size);
+ } else {
+ if (mapped_zip.GetBasePtr() == nullptr) {
+ ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer");
+ return false;
+ }
+ if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
+ mapped_zip.GetFileLength()) {
+ ALOGE(
+ "Zip: Failed to map central directory, offset exceeds mapped memory region ("
+ "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
+ static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
+ return false;
+ }
+
+ central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
+ }
+ return true;
+}
+
+// This function returns the embedded timestamp as is; and doesn't perform validations.
+tm ZipEntry::GetModificationTime() const {
+ tm t = {};
+
+ t.tm_hour = (mod_time >> 11) & 0x1f;
+ t.tm_min = (mod_time >> 5) & 0x3f;
+ t.tm_sec = (mod_time & 0x1f) << 1;
+
+ t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
+ t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
+ t.tm_mday = (mod_time >> 16) & 0x1f;
+
+ return t;
+}
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
new file mode 100644
index 000000000..cfa5912a2
--- /dev/null
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <benchmark/benchmark.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <ziparchive/zip_writer.h>
+
+static std::unique_ptr<TemporaryFile> CreateZip(int size = 4, int count = 1000) {
+ auto result = std::make_unique<TemporaryFile>();
+ FILE* fp = fdopen(result->fd, "w");
+
+ ZipWriter writer(fp);
+ std::string lastName = "file";
+ for (size_t i = 0; i < count; i++) {
+ // Make file names longer and longer.
+ lastName = lastName + std::to_string(i);
+ writer.StartEntry(lastName.c_str(), ZipWriter::kCompress);
+ while (size > 0) {
+ writer.WriteBytes("helo", 4);
+ size -= 4;
+ }
+ writer.FinishEntry();
+ }
+ writer.Finish();
+ fclose(fp);
+
+ return result;
+}
+
+static void FindEntry_no_match(benchmark::State& state) {
+ // Create a temporary zip archive.
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ ZipEntry data;
+
+ // In order to walk through all file names in the archive, look for a name
+ // that does not exist in the archive.
+ std::string_view name("thisFileNameDoesNotExist");
+
+ // Start the benchmark.
+ for (auto _ : state) {
+ OpenArchive(temp_file->path, &handle);
+ FindEntry(handle, name, &data);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(FindEntry_no_match);
+
+static void Iterate_all_files(benchmark::State& state) {
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip());
+ ZipArchiveHandle handle;
+ void* iteration_cookie;
+ ZipEntry data;
+ std::string name;
+
+ for (auto _ : state) {
+ OpenArchive(temp_file->path, &handle);
+ StartIteration(handle, &iteration_cookie);
+ while (Next(iteration_cookie, &data, &name) == 0) {
+ }
+ EndIteration(iteration_cookie);
+ CloseArchive(handle);
+ }
+}
+BENCHMARK(Iterate_all_files);
+
+static void StartAlignedEntry(benchmark::State& state) {
+ TemporaryFile file;
+ FILE* fp = fdopen(file.fd, "w");
+
+ ZipWriter writer(fp);
+
+ auto alignment = uint32_t(state.range(0));
+ std::string name = "name";
+ int counter = 0;
+ for (auto _ : state) {
+ writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
+ state.PauseTiming();
+ writer.WriteBytes("hola", 4);
+ writer.FinishEntry();
+ state.ResumeTiming();
+ }
+
+ writer.Finish();
+ fclose(fp);
+}
+BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
+
+static void ExtractEntry(benchmark::State& state) {
+ std::unique_ptr<TemporaryFile> temp_file(CreateZip(1024 * 1024, 1));
+
+ ZipArchiveHandle handle;
+ ZipEntry data;
+ if (OpenArchive(temp_file->path, &handle)) {
+ state.SkipWithError("Failed to open archive");
+ }
+ if (FindEntry(handle, "file0", &data)) {
+ state.SkipWithError("Failed to find archive entry");
+ }
+
+ std::vector<uint8_t> buffer(1024 * 1024);
+ for (auto _ : state) {
+ if (ExtractToMemory(handle, &data, buffer.data(), uint32_t(buffer.size()))) {
+ state.SkipWithError("Failed to extract archive entry");
+ break;
+ }
+ }
+ CloseArchive(handle);
+}
+
+BENCHMARK(ExtractEntry)->Arg(2)->Arg(16)->Arg(1024);
+
+BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
new file mode 100644
index 000000000..8b99bde91
--- /dev/null
+++ b/libziparchive/zip_archive_common.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+
+#include "android-base/macros.h"
+
+#include <inttypes.h>
+
+// The "end of central directory" (EOCD) record. Each archive
+// contains exactly once such record which appears at the end of
+// the archive. It contains archive wide information like the
+// number of entries in the archive and the offset to the central
+// directory of the offset.
+struct EocdRecord {
+ static const uint32_t kSignature = 0x06054b50;
+
+ // End of central directory signature, should always be
+ // |kSignature|.
+ uint32_t eocd_signature;
+ // The number of the current "disk", i.e, the "disk" that this
+ // central directory is on.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that disk_num == 1.
+ uint16_t disk_num;
+ // The disk where the central directory starts.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that cd_start_disk == 1.
+ uint16_t cd_start_disk;
+ // The number of central directory records on this disk.
+ //
+ // This implementation assumes that each archive spans a single
+ // disk only. i.e, that num_records_on_disk == num_records.
+ uint16_t num_records_on_disk;
+ // The total number of central directory records.
+ uint16_t num_records;
+ // The size of the central directory (in bytes).
+ uint32_t cd_size;
+ // The offset of the start of the central directory, relative
+ // to the start of the file.
+ uint32_t cd_start_offset;
+ // Length of the central directory comment.
+ uint16_t comment_length;
+
+ private:
+ EocdRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(EocdRecord);
+} __attribute__((packed));
+
+// A structure representing the fixed length fields for a single
+// record in the central directory of the archive. In addition to
+// the fixed length fields listed here, each central directory
+// record contains a variable length "file_name" and "extra_field"
+// whose lengths are given by |file_name_length| and |extra_field_length|
+// respectively.
+struct CentralDirectoryRecord {
+ static const uint32_t kSignature = 0x02014b50;
+
+ // The start of record signature. Must be |kSignature|.
+ uint32_t record_signature;
+ // Source tool version. Top byte gives source OS.
+ uint16_t version_made_by;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ // The length of the entry comment (in bytes). This data will
+ // appear immediately after the extra field.
+ uint16_t comment_length;
+ // The start disk for this entry. Ignored by this implementation).
+ uint16_t file_start_disk;
+ // File attributes. Ignored by this implementation.
+ uint16_t internal_file_attributes;
+ // File attributes. For archives created on Unix, the top bits are the mode.
+ uint32_t external_file_attributes;
+ // The offset to the local file header for this entry, from the
+ // beginning of this archive.
+ uint32_t local_file_header_offset;
+
+ private:
+ CentralDirectoryRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+} __attribute__((packed));
+
+// The local file header for a given entry. This duplicates information
+// present in the central directory of the archive. It is an error for
+// the information here to be different from the central directory
+// information for a given entry.
+struct LocalFileHeader {
+ static const uint32_t kSignature = 0x04034b50;
+
+ // The local file header signature, must be |kSignature|.
+ uint32_t lfh_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+
+ private:
+ LocalFileHeader() = default;
+ DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+} __attribute__((packed));
+
+struct DataDescriptor {
+ // The *optional* data descriptor start signature.
+ static const uint32_t kOptSignature = 0x08074b50;
+
+ // CRC-32 checksum of the entry.
+ uint32_t crc32;
+ // Compressed size of the entry.
+ uint32_t compressed_size;
+ // Uncompressed size of the entry.
+ uint32_t uncompressed_size;
+
+ private:
+ DataDescriptor() = default;
+ DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+} __attribute__((packed));
+
+// mask value that signifies that the entry has a DD
+static const uint32_t kGPBDDFlagMask = 0x0008;
+
+// The maximum size of a central directory or a file
+// comment in bytes.
+static const uint32_t kMaxCommentLen = 65535;
+
+#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
new file mode 100644
index 000000000..362503816
--- /dev/null
+++ b/libziparchive/zip_archive_private.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ziparchive/zip_archive.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include "android-base/macros.h"
+#include "android-base/mapped_file.h"
+
+static const char* kErrorMessages[] = {
+ "Success",
+ "Iteration ended",
+ "Zlib error",
+ "Invalid file",
+ "Invalid handle",
+ "Duplicate entries in archive",
+ "Empty archive",
+ "Entry not found",
+ "Invalid offset",
+ "Inconsistent information",
+ "Invalid entry name",
+ "I/O error",
+ "File mapping failed",
+ "Allocation failed",
+};
+
+enum ErrorCodes : int32_t {
+ kIterationEnd = -1,
+
+ // We encountered a Zlib error when inflating a stream from this file.
+ // Usually indicates file corruption.
+ kZlibError = -2,
+
+ // The input file cannot be processed as a zip archive. Usually because
+ // it's too small, too large or does not have a valid signature.
+ kInvalidFile = -3,
+
+ // An invalid iteration / ziparchive handle was passed in as an input
+ // argument.
+ kInvalidHandle = -4,
+
+ // The zip archive contained two (or possibly more) entries with the same
+ // name.
+ kDuplicateEntry = -5,
+
+ // The zip archive contains no entries.
+ kEmptyArchive = -6,
+
+ // The specified entry was not found in the archive.
+ kEntryNotFound = -7,
+
+ // The zip archive contained an invalid local file header pointer.
+ kInvalidOffset = -8,
+
+ // The zip archive contained inconsistent entry information. This could
+ // be because the central directory & local file header did not agree, or
+ // if the actual uncompressed length or crc32 do not match their declared
+ // values.
+ kInconsistentInformation = -9,
+
+ // An invalid entry name was encountered.
+ kInvalidEntryName = -10,
+
+ // An I/O related system call (read, lseek, ftruncate, map) failed.
+ kIoError = -11,
+
+ // We were not able to mmap the central directory or entry contents.
+ kMmapFailed = -12,
+
+ // An allocation failed.
+ kAllocationFailed = -13,
+
+ kLastErrorCode = kAllocationFailed,
+};
+
+class MappedZipFile {
+ public:
+ explicit MappedZipFile(const int fd)
+ : has_fd_(true), fd_(fd), fd_offset_(0), base_ptr_(nullptr), data_length_(-1) {}
+
+ explicit MappedZipFile(const int fd, off64_t length, off64_t offset)
+ : has_fd_(true), fd_(fd), fd_offset_(offset), base_ptr_(nullptr), data_length_(length) {}
+
+ explicit MappedZipFile(const void* address, size_t length)
+ : has_fd_(false), fd_(-1), fd_offset_(0), base_ptr_(address),
+ data_length_(static_cast<off64_t>(length)) {}
+
+ bool HasFd() const { return has_fd_; }
+
+ int GetFileDescriptor() const;
+
+ const void* GetBasePtr() const;
+
+ off64_t GetFileOffset() const;
+
+ off64_t GetFileLength() const;
+
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
+
+ private:
+ // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
+ // from the file. Otherwise, we're opening the archive from a memory mapped
+ // file. In that case, base_ptr_ points to the start of the memory region and
+ // data_length_ defines the file length.
+ const bool has_fd_;
+
+ const int fd_;
+ const off64_t fd_offset_;
+
+ const void* const base_ptr_;
+ mutable off64_t data_length_;
+};
+
+class CentralDirectory {
+ public:
+ CentralDirectory(void) : base_ptr_(nullptr), length_(0) {}
+
+ const uint8_t* GetBasePtr() const { return base_ptr_; }
+
+ size_t GetMapLength() const { return length_; }
+
+ void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
+
+ private:
+ const uint8_t* base_ptr_;
+ size_t length_;
+};
+
+/**
+ * More space efficient string representation of strings in an mmaped zipped
+ * file than std::string_view. Using std::string_view as an entry in the
+ * ZipArchive hash table wastes space. std::string_view stores a pointer to a
+ * string (on 64 bit, 8 bytes) and the length to read from that pointer,
+ * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
+ * 6 bytes.
+ *
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
+ * mapped file instead of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+ uint32_t name_offset;
+ uint16_t name_length;
+
+ const std::string_view ToStringView(const uint8_t* start) const {
+ return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
+ }
+};
+
+struct ZipArchive {
+ // open Zip archive
+ mutable MappedZipFile mapped_zip;
+ const bool close_file;
+
+ // mapped central directory area
+ off64_t directory_offset;
+ CentralDirectory central_directory;
+ std::unique_ptr<android::base::MappedFile> directory_map;
+
+ // number of entries in the Zip archive
+ uint16_t num_entries;
+
+ // We know how many entries are in the Zip archive, so we can have a
+ // fixed-size hash table. We define a load factor of 0.75 and over
+ // allocate so the maximum number entries can never be higher than
+ // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
+ uint32_t hash_table_size;
+ ZipStringOffset* hash_table;
+
+ ZipArchive(MappedZipFile&& map, bool assume_ownership);
+ ZipArchive(const void* address, size_t length);
+ ~ZipArchive();
+
+ bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
+};
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
new file mode 100644
index 000000000..1ec95b6eb
--- /dev/null
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ZIPARCHIVE"
+
+// Read-only stream access to Zip Archive entries.
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+#include <zlib.h>
+
+#include "zip_archive_private.h"
+
+static constexpr size_t kBufSize = 65535;
+
+bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
+ crc32_ = entry.crc32;
+ offset_ = entry.offset;
+ return true;
+}
+
+class ZipArchiveStreamEntryUncompressed : public ZipArchiveStreamEntry {
+ public:
+ explicit ZipArchiveStreamEntryUncompressed(ZipArchiveHandle handle)
+ : ZipArchiveStreamEntry(handle) {}
+ virtual ~ZipArchiveStreamEntryUncompressed() {}
+
+ const std::vector<uint8_t>* Read() override;
+
+ bool Verify() override;
+
+ protected:
+ bool Init(const ZipEntry& entry) override;
+
+ uint32_t length_ = 0u;
+
+ private:
+ std::vector<uint8_t> data_;
+ uint32_t computed_crc32_ = 0u;
+};
+
+bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
+ if (!ZipArchiveStreamEntry::Init(entry)) {
+ return false;
+ }
+
+ length_ = entry.uncompressed_length;
+
+ data_.resize(kBufSize);
+ computed_crc32_ = 0;
+
+ return true;
+}
+
+const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
+ // Simple sanity check. The vector should *only* be handled by this code. A caller
+ // should not const-cast and modify the capacity. This may invalidate next_out.
+ //
+ // Note: it would be better to store the results of data() across Read calls.
+ CHECK_EQ(data_.capacity(), kBufSize);
+
+ if (length_ == 0) {
+ return nullptr;
+ }
+
+ size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+ errno = 0;
+ if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
+ if (errno != 0) {
+ ALOGE("Error reading from archive fd: %s", strerror(errno));
+ } else {
+ ALOGE("Short read of zip file, possibly corrupted zip?");
+ }
+ length_ = 0;
+ return nullptr;
+ }
+
+ if (bytes < data_.size()) {
+ data_.resize(bytes);
+ }
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
+ length_ -= bytes;
+ offset_ += bytes;
+ return &data_;
+}
+
+bool ZipArchiveStreamEntryUncompressed::Verify() {
+ return length_ == 0 && crc32_ == computed_crc32_;
+}
+
+class ZipArchiveStreamEntryCompressed : public ZipArchiveStreamEntry {
+ public:
+ explicit ZipArchiveStreamEntryCompressed(ZipArchiveHandle handle)
+ : ZipArchiveStreamEntry(handle) {}
+ virtual ~ZipArchiveStreamEntryCompressed();
+
+ const std::vector<uint8_t>* Read() override;
+
+ bool Verify() override;
+
+ protected:
+ bool Init(const ZipEntry& entry) override;
+
+ private:
+ bool z_stream_init_ = false;
+ z_stream z_stream_;
+ std::vector<uint8_t> in_;
+ std::vector<uint8_t> out_;
+ uint32_t uncompressed_length_ = 0u;
+ uint32_t compressed_length_ = 0u;
+ uint32_t computed_crc32_ = 0u;
+};
+
+// This method is using libz macros with old-style-casts
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
+ return inflateInit2(stream, window_bits);
+}
+#pragma GCC diagnostic pop
+
+bool ZipArchiveStreamEntryCompressed::Init(const ZipEntry& entry) {
+ if (!ZipArchiveStreamEntry::Init(entry)) {
+ return false;
+ }
+
+ // Initialize the zlib stream struct.
+ memset(&z_stream_, 0, sizeof(z_stream_));
+ z_stream_.zalloc = Z_NULL;
+ z_stream_.zfree = Z_NULL;
+ z_stream_.opaque = Z_NULL;
+ z_stream_.next_in = nullptr;
+ z_stream_.avail_in = 0;
+ z_stream_.avail_out = 0;
+ z_stream_.data_type = Z_UNKNOWN;
+
+ // Use the undocumented "negative window bits" feature to tell zlib
+ // that there's no zlib header waiting for it.
+ int zerr = zlib_inflateInit2(&z_stream_, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)", zerr);
+ }
+
+ return false;
+ }
+
+ z_stream_init_ = true;
+
+ uncompressed_length_ = entry.uncompressed_length;
+ compressed_length_ = entry.compressed_length;
+
+ out_.resize(kBufSize);
+ in_.resize(kBufSize);
+
+ computed_crc32_ = 0;
+
+ return true;
+}
+
+ZipArchiveStreamEntryCompressed::~ZipArchiveStreamEntryCompressed() {
+ if (z_stream_init_) {
+ inflateEnd(&z_stream_);
+ z_stream_init_ = false;
+ }
+}
+
+bool ZipArchiveStreamEntryCompressed::Verify() {
+ return z_stream_init_ && uncompressed_length_ == 0 && compressed_length_ == 0 &&
+ crc32_ == computed_crc32_;
+}
+
+const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
+ // Simple sanity check. The vector should *only* be handled by this code. A caller
+ // should not const-cast and modify the capacity. This may invalidate next_out.
+ //
+ // Note: it would be better to store the results of data() across Read calls.
+ CHECK_EQ(out_.capacity(), kBufSize);
+
+ if (z_stream_.avail_out == 0) {
+ z_stream_.next_out = out_.data();
+ z_stream_.avail_out = static_cast<uint32_t>(out_.size());
+ ;
+ }
+
+ while (true) {
+ if (z_stream_.avail_in == 0) {
+ if (compressed_length_ == 0) {
+ return nullptr;
+ }
+ DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
+ uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
+ : compressed_length_;
+ ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
+ errno = 0;
+ if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
+ if (errno != 0) {
+ ALOGE("Error reading from archive fd: %s", strerror(errno));
+ } else {
+ ALOGE("Short read of zip file, possibly corrupted zip?");
+ }
+ return nullptr;
+ }
+
+ compressed_length_ -= bytes;
+ offset_ += bytes;
+ z_stream_.next_in = in_.data();
+ z_stream_.avail_in = bytes;
+ }
+
+ int zerr = inflate(&z_stream_, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGE("inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", zerr, z_stream_.next_in,
+ z_stream_.avail_in, z_stream_.next_out, z_stream_.avail_out);
+ return nullptr;
+ }
+
+ if (z_stream_.avail_out == 0) {
+ uncompressed_length_ -= out_.size();
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
+ return &out_;
+ }
+ if (zerr == Z_STREAM_END) {
+ if (z_stream_.avail_out != 0) {
+ // Resize the vector down to the actual size of the data.
+ out_.resize(out_.size() - z_stream_.avail_out);
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
+ uncompressed_length_ -= out_.size();
+ return &out_;
+ }
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+class ZipArchiveStreamEntryRawCompressed : public ZipArchiveStreamEntryUncompressed {
+ public:
+ explicit ZipArchiveStreamEntryRawCompressed(ZipArchiveHandle handle)
+ : ZipArchiveStreamEntryUncompressed(handle) {}
+ virtual ~ZipArchiveStreamEntryRawCompressed() {}
+
+ bool Verify() override;
+
+ protected:
+ bool Init(const ZipEntry& entry) override;
+};
+
+bool ZipArchiveStreamEntryRawCompressed::Init(const ZipEntry& entry) {
+ if (!ZipArchiveStreamEntryUncompressed::Init(entry)) {
+ return false;
+ }
+ length_ = entry.compressed_length;
+
+ return true;
+}
+
+bool ZipArchiveStreamEntryRawCompressed::Verify() {
+ return length_ == 0;
+}
+
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::Create(ZipArchiveHandle handle,
+ const ZipEntry& entry) {
+ ZipArchiveStreamEntry* stream = nullptr;
+ if (entry.method != kCompressStored) {
+ stream = new ZipArchiveStreamEntryCompressed(handle);
+ } else {
+ stream = new ZipArchiveStreamEntryUncompressed(handle);
+ }
+ if (stream && !stream->Init(entry)) {
+ delete stream;
+ stream = nullptr;
+ }
+
+ return stream;
+}
+
+ZipArchiveStreamEntry* ZipArchiveStreamEntry::CreateRaw(ZipArchiveHandle handle,
+ const ZipEntry& entry) {
+ ZipArchiveStreamEntry* stream = nullptr;
+ if (entry.method == kCompressStored) {
+ // Not compressed, don't need to do anything special.
+ stream = new ZipArchiveStreamEntryUncompressed(handle);
+ } else {
+ stream = new ZipArchiveStreamEntryRawCompressed(handle);
+ }
+ if (stream && !stream->Init(entry)) {
+ delete stream;
+ stream = nullptr;
+ }
+ return stream;
+}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
new file mode 100644
index 000000000..35fb3fe0f
--- /dev/null
+++ b/libziparchive/zip_archive_test.cc
@@ -0,0 +1,859 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "zip_archive_private.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/mapped_file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_archive_stream_entry.h>
+
+static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
+
+static const std::string kValidZip = "valid.zip";
+static const std::string kLargeZip = "large.zip";
+static const std::string kBadCrcZip = "bad_crc.zip";
+
+static const std::vector<uint8_t> kATxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a',
+ 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
+
+static const std::vector<uint8_t> kATxtContentsCompressed{'K', 'L', 'J', 'N', 'I', 'M', 'K',
+ 207, 'H', 132, 210, '\\', '\0'};
+
+static const std::vector<uint8_t> kBTxtContents{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\n'};
+
+static int32_t OpenArchiveWrapper(const std::string& name, ZipArchiveHandle* handle) {
+ const std::string abs_path = test_data_dir + "/" + name;
+ return OpenArchive(abs_path.c_str(), handle);
+}
+
+TEST(ziparchive, Open) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+ CloseArchive(handle);
+
+ ASSERT_EQ(kInvalidEntryName, OpenArchiveWrapper("bad_filename.zip", &handle));
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, OutOfBound) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(kInvalidOffset, OpenArchiveWrapper("crash.apk", &handle));
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, EmptyArchive) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(kEmptyArchive, OpenArchiveWrapper("empty.zip", &handle));
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, ZeroSizeCentralDirectory) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(kInvalidFile, OpenArchiveWrapper("zero-size-cd.zip", &handle));
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, OpenMissing) {
+ ZipArchiveHandle handle;
+ ASSERT_NE(0, OpenArchiveWrapper("missing.zip", &handle));
+
+ // Confirm the file descriptor is not going to be mistaken for a valid one.
+ ASSERT_EQ(-1, GetFileDescriptor(handle));
+}
+
+TEST(ziparchive, OpenAssumeFdOwnership) {
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+ ASSERT_NE(-1, fd);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
+ CloseArchive(handle);
+ ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
+ ASSERT_EQ(EBADF, errno);
+}
+
+TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+ ASSERT_NE(-1, fd);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
+ CloseArchive(handle);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
+ close(fd);
+}
+
+TEST(ziparchive, OpenAssumeFdRangeOwnership) {
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+ ASSERT_NE(-1, fd);
+ const off64_t length = lseek64(fd, 0, SEEK_END);
+ ASSERT_NE(-1, length);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
+ static_cast<size_t>(length), 0));
+ CloseArchive(handle);
+ ASSERT_EQ(-1, lseek(fd, 0, SEEK_SET));
+ ASSERT_EQ(EBADF, errno);
+}
+
+TEST(ziparchive, OpenDoNotAssumeFdRangeOwnership) {
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
+ ASSERT_NE(-1, fd);
+ const off64_t length = lseek(fd, 0, SEEK_END);
+ ASSERT_NE(-1, length);
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFdRange(fd, "OpenWithAssumeFdOwnership", &handle,
+ static_cast<size_t>(length), 0, false));
+ CloseArchive(handle);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
+ close(fd);
+}
+
+TEST(ziparchive, Iteration_std_string_view) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
+
+ ZipEntry data;
+ std::vector<std::string_view> names;
+ std::string_view name;
+ while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
+
+ // Assert that the names are as expected.
+ std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
+ std::sort(names.begin(), names.end());
+ ASSERT_EQ(expected_names, names);
+
+ CloseArchive(handle);
+}
+
+static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
+ const std::vector<std::string>& expected_names_sorted) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
+
+ ZipEntry data;
+ std::vector<std::string> names;
+
+ std::string name;
+ for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ names.push_back(name);
+ }
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+ CloseArchive(handle);
+
+ // Assert that the names are as expected.
+ std::sort(names.begin(), names.end());
+ ASSERT_EQ(expected_names_sorted, names);
+}
+
+TEST(ziparchive, Iteration) {
+ static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
+ "b/d.txt"};
+
+ AssertIterationOrder("", "", kExpectedMatchesSorted);
+}
+
+TEST(ziparchive, IterationWithPrefix) {
+ static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
+
+ AssertIterationOrder("b/", "", kExpectedMatchesSorted);
+}
+
+TEST(ziparchive, IterationWithSuffix) {
+ static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
+ "b/d.txt"};
+
+ AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
+}
+
+TEST(ziparchive, IterationWithPrefixAndSuffix) {
+ static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
+
+ AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
+}
+
+TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
+
+ ZipEntry data;
+ std::string name;
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+
+ // Known facts about a.txt, from zipinfo -v.
+ ASSERT_EQ(63, data.offset);
+ ASSERT_EQ(kCompressDeflated, data.method);
+ ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
+ ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
+ ASSERT_EQ(0x950821c5, data.crc32);
+ ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
+
+ // An entry that doesn't exist. Should be a negative return code.
+ ASSERT_LT(FindEntry(handle, "this file does not exist", &data), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ std::string very_long_name(65536, 'x');
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, TestInvalidDeclaredLength) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
+
+ std::string name;
+ ZipEntry data;
+
+ ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
+ ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, OpenArchiveFdRange) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+
+ const std::string leading_garbage(21, 'x');
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, leading_garbage.c_str(),
+ leading_garbage.size()));
+
+ std::string valid_content;
+ ASSERT_TRUE(android::base::ReadFileToString(test_data_dir + "/" + kValidZip, &valid_content));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, valid_content.c_str(), valid_content.size()));
+
+ const std::string ending_garbage(42, 'x');
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, ending_garbage.c_str(),
+ ending_garbage.size()));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_EQ(0, OpenArchiveFdRange(tmp_file.fd, "OpenArchiveFdRange", &handle,
+ valid_content.size(),
+ static_cast<off64_t>(leading_garbage.size())));
+
+ // An entry that's deflated.
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+ const uint32_t a_size = data.uncompressed_length;
+ ASSERT_EQ(a_size, kATxtContents.size());
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[a_size]);
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), a_size));
+ ASSERT_EQ(0, memcmp(buffer.get(), kATxtContents.data(), a_size));
+
+ // An entry that's stored.
+ ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
+ const uint32_t b_size = data.uncompressed_length;
+ ASSERT_EQ(b_size, kBTxtContents.size());
+ buffer = std::unique_ptr<uint8_t[]>(new uint8_t[b_size]);
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer.get(), b_size));
+ ASSERT_EQ(0, memcmp(buffer.get(), kBTxtContents.data(), b_size));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, ExtractToMemory) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ // An entry that's deflated.
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+ const uint32_t a_size = data.uncompressed_length;
+ ASSERT_EQ(a_size, kATxtContents.size());
+ uint8_t* buffer = new uint8_t[a_size];
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
+ ASSERT_EQ(0, memcmp(buffer, kATxtContents.data(), a_size));
+ delete[] buffer;
+
+ // An entry that's stored.
+ ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
+ const uint32_t b_size = data.uncompressed_length;
+ ASSERT_EQ(b_size, kBTxtContents.size());
+ buffer = new uint8_t[b_size];
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
+ ASSERT_EQ(0, memcmp(buffer, kBTxtContents.data(), b_size));
+ delete[] buffer;
+
+ CloseArchive(handle);
+}
+
+static const uint32_t kEmptyEntriesZip[] = {
+ 0x04034b50, 0x0000000a, 0x63600000, 0x00004438, 0x00000000, 0x00000000, 0x00090000,
+ 0x6d65001c, 0x2e797470, 0x55747874, 0x03000954, 0x52e25c13, 0x52e25c24, 0x000b7875,
+ 0x42890401, 0x88040000, 0x50000013, 0x1e02014b, 0x00000a03, 0x60000000, 0x00443863,
+ 0x00000000, 0x00000000, 0x09000000, 0x00001800, 0x00000000, 0xa0000000, 0x00000081,
+ 0x706d6500, 0x742e7974, 0x54557478, 0x13030005, 0x7552e25c, 0x01000b78, 0x00428904,
+ 0x13880400, 0x4b500000, 0x00000605, 0x00010000, 0x004f0001, 0x00430000, 0x00000000};
+
+// This is a zip file containing a single entry (ab.txt) that contains
+// 90072 repetitions of the string "ab\n" and has an uncompressed length
+// of 270216 bytes.
+static const uint16_t kAbZip[] = {
+ 0x4b50, 0x0403, 0x0014, 0x0000, 0x0008, 0x51d2, 0x4698, 0xc4b0, 0x2cda, 0x011b, 0x0000, 0x1f88,
+ 0x0004, 0x0006, 0x001c, 0x6261, 0x742e, 0x7478, 0x5455, 0x0009, 0x7c03, 0x3a09, 0x7c55, 0x3a09,
+ 0x7555, 0x0b78, 0x0100, 0x8904, 0x0042, 0x0400, 0x1388, 0x0000, 0xc2ed, 0x0d31, 0x0000, 0x030c,
+ 0x7fa0, 0x3b2e, 0x22ff, 0xa2aa, 0x841f, 0x45fc, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555, 0x5555,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xdd55, 0x502c, 0x014b, 0x1e02, 0x1403, 0x0000, 0x0800, 0xd200,
+ 0x9851, 0xb046, 0xdac4, 0x1b2c, 0x0001, 0x8800, 0x041f, 0x0600, 0x1800, 0x0000, 0x0000, 0x0100,
+ 0x0000, 0xa000, 0x0081, 0x0000, 0x6100, 0x2e62, 0x7874, 0x5574, 0x0554, 0x0300, 0x097c, 0x553a,
+ 0x7875, 0x000b, 0x0401, 0x4289, 0x0000, 0x8804, 0x0013, 0x5000, 0x054b, 0x0006, 0x0000, 0x0100,
+ 0x0100, 0x4c00, 0x0000, 0x5b00, 0x0001, 0x0000, 0x0000};
+
+static const std::string kAbTxtName("ab.txt");
+static const size_t kAbUncompressedSize = 270216;
+
+TEST(ziparchive, EmptyEntries) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
+
+ ZipEntry entry;
+ ASSERT_EQ(0, FindEntry(handle, "empty.txt", &entry));
+ ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
+ uint8_t buffer[1];
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
+
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
+
+ struct stat stat_buf;
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
+ ASSERT_EQ(0, stat_buf.st_size);
+}
+
+TEST(ziparchive, EntryLargerThan32K) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
+ sizeof(kAbZip) - 1));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
+
+ ZipEntry entry;
+ ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
+ ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
+
+ // Extract the entry to memory.
+ std::vector<uint8_t> buffer(kAbUncompressedSize);
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
+
+ // Extract the entry to a file.
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
+
+ // Make sure the extracted file size is as expected.
+ struct stat stat_buf;
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
+ ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
+
+ // Read the file back to a buffer and make sure the contents are
+ // the same as the memory buffer we extracted directly to.
+ std::vector<uint8_t> file_contents(kAbUncompressedSize);
+ ASSERT_EQ(0, lseek(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0], file_contents.size()));
+ ASSERT_EQ(file_contents, buffer);
+
+ for (int i = 0; i < 90072; ++i) {
+ const uint8_t* line = &file_contents[0] + (3 * i);
+ ASSERT_EQ('a', line[0]);
+ ASSERT_EQ('b', line[1]);
+ ASSERT_EQ('\n', line[2]);
+ }
+}
+
+TEST(ziparchive, TrailerAfterEOCD) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+
+ // Create a file with 8 bytes of random garbage.
+ static const uint8_t trailer[] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'z'};
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
+
+ ZipArchiveHandle handle;
+ ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
+}
+
+TEST(ziparchive, ExtractToFile) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ const uint8_t data[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
+ const size_t data_size = sizeof(data);
+
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry entry;
+ ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
+
+ // Assert that the first 8 bytes of the file haven't been clobbered.
+ uint8_t read_buffer[data_size];
+ ASSERT_EQ(0, lseek(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
+ ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
+
+ // Assert that the remainder of the file contains the incompressed data.
+ std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
+ ASSERT_TRUE(
+ android::base::ReadFully(tmp_file.fd, uncompressed_data.data(), entry.uncompressed_length));
+ ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(), kATxtContents.size()));
+
+ // Assert that the total length of the file is sane
+ ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
+ lseek(tmp_file.fd, 0, SEEK_END));
+}
+
+#if !defined(_WIN32)
+TEST(ziparchive, OpenFromMemory) {
+ const std::string zip_path = test_data_dir + "/dummy-update.zip";
+ android::base::unique_fd fd(open(zip_path.c_str(), O_RDONLY | O_BINARY));
+ ASSERT_NE(-1, fd);
+ struct stat sb;
+ ASSERT_EQ(0, fstat(fd, &sb));
+
+ // Memory map the file first and open the archive from the memory region.
+ auto file_map{
+ android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0,
+ OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
+
+ // Assert one entry can be found and extracted correctly.
+ ZipEntry binary_entry;
+ ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
+ TemporaryFile tmp_binary;
+ ASSERT_NE(-1, tmp_binary.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
+}
+#endif
+
+static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
+ bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
+ ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
+ std::unique_ptr<ZipArchiveStreamEntry> stream;
+ if (raw) {
+ stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
+ if (entry->method == kCompressStored) {
+ read_data->resize(entry->uncompressed_length);
+ } else {
+ read_data->resize(entry->compressed_length);
+ }
+ } else {
+ stream.reset(ZipArchiveStreamEntry::Create(handle, *entry));
+ read_data->resize(entry->uncompressed_length);
+ }
+ uint8_t* read_data_ptr = read_data->data();
+ ASSERT_TRUE(stream.get() != nullptr);
+ const std::vector<uint8_t>* data;
+ uint64_t total_size = 0;
+ while ((data = stream->Read()) != nullptr) {
+ total_size += data->size();
+ memcpy(read_data_ptr, data->data(), data->size());
+ read_data_ptr += data->size();
+ }
+ ASSERT_EQ(verified, stream->Verify());
+ ASSERT_EQ(total_size, read_data->size());
+}
+
+static void ZipArchiveStreamTestUsingContents(const std::string& zip_file,
+ const std::string& entry_name,
+ const std::vector<uint8_t>& contents, bool raw) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, entry_name, raw, true, &entry, &read_data);
+
+ ASSERT_EQ(contents.size(), read_data.size());
+ ASSERT_TRUE(memcmp(read_data.data(), contents.data(), read_data.size()) == 0);
+
+ CloseArchive(handle);
+}
+
+static void ZipArchiveStreamTestUsingMemory(const std::string& zip_file,
+ const std::string& entry_name) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(zip_file, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, entry_name, false, true, &entry, &read_data);
+
+ std::vector<uint8_t> cmp_data(entry.uncompressed_length);
+ ASSERT_EQ(entry.uncompressed_length, read_data.size());
+ ASSERT_EQ(
+ 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
+ ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, StreamCompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContents, false);
+}
+
+TEST(ziparchive, StreamUncompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, false);
+}
+
+TEST(ziparchive, StreamRawCompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, "a.txt", kATxtContentsCompressed, true);
+}
+
+TEST(ziparchive, StreamRawUncompressed) {
+ ZipArchiveStreamTestUsingContents(kValidZip, "b.txt", kBTxtContents, true);
+}
+
+TEST(ziparchive, StreamLargeCompressed) {
+ ZipArchiveStreamTestUsingMemory(kLargeZip, "compress.txt");
+}
+
+TEST(ziparchive, StreamLargeUncompressed) {
+ ZipArchiveStreamTestUsingMemory(kLargeZip, "uncompress.txt");
+}
+
+TEST(ziparchive, StreamCompressedBadCrc) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, "a.txt", false, false, &entry, &read_data);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, StreamUncompressedBadCrc) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kBadCrcZip, &handle));
+
+ ZipEntry entry;
+ std::vector<uint8_t> read_data;
+ ZipArchiveStreamTest(handle, "b.txt", false, false, &entry, &read_data);
+
+ CloseArchive(handle);
+}
+
+// Generated using the following Java program:
+// public static void main(String[] foo) throws Exception {
+// FileOutputStream fos = new
+// FileOutputStream("/tmp/data_descriptor.zip");
+// ZipOutputStream zos = new ZipOutputStream(fos);
+// ZipEntry ze = new ZipEntry("name");
+// ze.setMethod(ZipEntry.DEFLATED);
+// zos.putNextEntry(ze);
+// zos.write("abdcdefghijk".getBytes());
+// zos.closeEntry();
+// zos.close();
+// }
+//
+// cat /tmp/data_descriptor.zip | xxd -i
+//
+static const std::vector<uint8_t> kDataDescriptorZipFile{
+ 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x61,
+ 0x6d, 0x65, 0x4b, 0x4c, 0x4a, 0x49, 0x4e, 0x49, 0x4d, 0x4b, 0xcf, 0xc8, 0xcc, 0xca, 0x06, 0x00,
+ //[sig---------------], [crc32---------------], [csize---------------], [size----------------]
+ 0x50, 0x4b, 0x07, 0x08, 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x30, 0x59, 0xce, 0x4a,
+ 0x3d, 0x4e, 0x0e, 0xf9, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61,
+ 0x6d, 0x65, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x32, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+// The offsets of the data descriptor in this file, so we can mess with
+// them later in the test.
+static constexpr uint32_t kDataDescriptorOffset = 48;
+static constexpr uint32_t kCSizeOffset = kDataDescriptorOffset + 8;
+static constexpr uint32_t kSizeOffset = kCSizeOffset + 4;
+
+static void ExtractEntryToMemory(const std::vector<uint8_t>& zip_data,
+ std::vector<uint8_t>* entry_out, int32_t* error_code_out) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
+
+ // This function expects a variant of kDataDescriptorZipFile, for look for
+ // an entry whose name is "name" and whose size is 12 (contents =
+ // "abdcdefghijk").
+ ZipEntry entry;
+ ASSERT_EQ(0, FindEntry(handle, "name", &entry));
+ ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
+
+ entry_out->resize(12);
+ (*error_code_out) = ExtractToMemory(handle, &entry, &((*entry_out)[0]), 12);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, ValidDataDescriptors) {
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(kDataDescriptorZipFile, &entry, &error_code);
+
+ ASSERT_EQ(0, error_code);
+ ASSERT_EQ(12u, entry.size());
+ ASSERT_EQ('a', entry[0]);
+ ASSERT_EQ('k', entry[11]);
+}
+
+TEST(ziparchive, InvalidDataDescriptors_csize) {
+ std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
+ invalid_csize[kCSizeOffset] = 0xfe;
+
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+
+ ASSERT_EQ(kInconsistentInformation, error_code);
+}
+
+TEST(ziparchive, InvalidDataDescriptors_size) {
+ std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
+ invalid_size[kSizeOffset] = 0xfe;
+
+ std::vector<uint8_t> entry;
+ int32_t error_code = 0;
+ ExtractEntryToMemory(invalid_size, &entry, &error_code);
+
+ ASSERT_EQ(kInconsistentInformation, error_code);
+}
+
+TEST(ziparchive, ErrorCodeString) {
+ ASSERT_STREQ("Success", ErrorCodeString(0));
+
+ // Out of bounds.
+ ASSERT_STREQ("Unknown return code", ErrorCodeString(1));
+ ASSERT_STRNE("Unknown return code", ErrorCodeString(kLastErrorCode));
+ ASSERT_STREQ("Unknown return code", ErrorCodeString(kLastErrorCode - 1));
+
+ ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
+}
+
+// A zip file whose local file header at offset zero is corrupted.
+//
+// ---------------
+// cat foo > a.txt
+// zip a.zip a.txt
+// cat a.zip | xxd -i
+//
+// Manual changes :
+// [2] = 0xff // Corrupt the LFH signature of entry 0.
+// [3] = 0xff // Corrupt the LFH signature of entry 0.
+static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
+ //[lfh-sig-----------], [lfh contents---------------------------------
+ 0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
+ //--------------------------------------------------------------------
+ 0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
+ //-------------------------------] [file-name-----------------], [---
+ 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
+ // entry-contents------------------------------------------------------
+ 0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
+ //--------------------------------------------------------------------
+ 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
+ //-------------------------------------], [cd-record-sig-------], [---
+ 0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
+ // cd-record-----------------------------------------------------------
+ 0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
+ //--------------------------------------------------------------------
+ 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
+ //--------------------------------------------------------------------
+ 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
+ //-] [lfh-file-header-off-], [file-name-----------------], [extra----
+ 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
+ //--------------------------------------------------------------------
+ 0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
+ //-------------------------------------------------------], [eocd-sig-
+ 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
+ //-------], [---------------------------------------------------------
+ 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
+ //-------------------------------------------]
+ 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+TEST(ziparchive, BrokenLfhSignature) {
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
+ kZipFileWithBrokenLfhSignature.size()));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(kInvalidFile, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
+}
+
+class VectorReader : public zip_archive::Reader {
+ public:
+ VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
+
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ if ((offset + len) < input_.size()) {
+ return false;
+ }
+
+ memcpy(buf, &input_[offset], len);
+ return true;
+ }
+
+ private:
+ const std::vector<uint8_t>& input_;
+};
+
+class VectorWriter : public zip_archive::Writer {
+ public:
+ VectorWriter() : Writer() {}
+
+ bool Append(uint8_t* buf, size_t size) {
+ output_.insert(output_.end(), buf, buf + size);
+ return true;
+ }
+
+ std::vector<uint8_t>& GetOutput() { return output_; }
+
+ private:
+ std::vector<uint8_t> output_;
+};
+
+class BadReader : public zip_archive::Reader {
+ public:
+ BadReader() : Reader() {}
+
+ bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
+};
+
+class BadWriter : public zip_archive::Writer {
+ public:
+ BadWriter() : Writer() {}
+
+ bool Append(uint8_t*, size_t) { return false; }
+};
+
+TEST(ziparchive, Inflate) {
+ const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
+ const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
+
+ const VectorReader reader(kATxtContentsCompressed);
+ {
+ VectorWriter writer;
+ uint64_t crc_out = 0;
+
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(kATxtContents, writer.GetOutput());
+ ASSERT_EQ(0x950821C5u, crc_out);
+ }
+
+ {
+ VectorWriter writer;
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(kATxtContents, writer.GetOutput());
+ }
+
+ {
+ BadWriter writer;
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+ ASSERT_EQ(kIoError, ret);
+ }
+
+ {
+ BadReader reader;
+ VectorWriter writer;
+ int32_t ret =
+ zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+ ASSERT_EQ(kIoError, ret);
+ ASSERT_EQ(0u, writer.GetOutput().size());
+ }
+}
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
new file mode 100644
index 000000000..67279a6b7
--- /dev/null
+++ b/libziparchive/zip_writer.cc
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ziparchive/zip_writer.h"
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <zlib.h>
+#include <cstdio>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+
+#include <memory>
+#include <vector>
+
+#include "android-base/logging.h"
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+
+#undef powerof2
+#define powerof2(x) \
+ ({ \
+ __typeof__(x) _x = (x); \
+ __typeof__(x) _x2; \
+ __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
+ })
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
+// No error, operation completed successfully.
+static const int32_t kNoError = 0;
+
+// The ZipWriter is in a bad state.
+static const int32_t kInvalidState = -1;
+
+// There was an IO error while writing to disk.
+static const int32_t kIoError = -2;
+
+// The zip entry name was invalid.
+static const int32_t kInvalidEntryName = -3;
+
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
+// The start aligned function was called with the aligned flag.
+static const int32_t kInvalidAlign32Flag = -5;
+
+// The alignment parameter is not a power of 2.
+static const int32_t kInvalidAlignment = -6;
+
+static const char* sErrorCodes[] = {
+ "Invalid state", "IO error", "Invalid entry name", "Zlib error",
+};
+
+const char* ZipWriter::ErrorCodeString(int32_t error_code) {
+ if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
+ return sErrorCodes[-error_code];
+ }
+ return nullptr;
+}
+
+static void DeleteZStream(z_stream* stream) {
+ deflateEnd(stream);
+ delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f)
+ : file_(f),
+ seekable_(false),
+ current_offset_(0),
+ state_(State::kWritingZip),
+ z_stream_(nullptr, DeleteZStream),
+ buffer_(kBufSize) {
+ // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
+ // will fail as well.
+ struct stat file_stats;
+ if (fstat(fileno(f), &file_stats) == 0) {
+ seekable_ = S_ISREG(file_stats.st_mode);
+ }
+}
+
+ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
+ : file_(writer.file_),
+ seekable_(writer.seekable_),
+ current_offset_(writer.current_offset_),
+ state_(writer.state_),
+ files_(std::move(writer.files_)),
+ z_stream_(std::move(writer.z_stream_)),
+ buffer_(std::move(writer.buffer_)) {
+ writer.file_ = nullptr;
+ writer.state_ = State::kError;
+}
+
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
+ file_ = writer.file_;
+ seekable_ = writer.seekable_;
+ current_offset_ = writer.current_offset_;
+ state_ = writer.state_;
+ files_ = std::move(writer.files_);
+ z_stream_ = std::move(writer.z_stream_);
+ buffer_ = std::move(writer.buffer_);
+ writer.file_ = nullptr;
+ writer.state_ = State::kError;
+ return *this;
+}
+
+int32_t ZipWriter::HandleError(int32_t error_code) {
+ state_ = State::kError;
+ z_stream_.reset();
+ return error_code;
+}
+
+int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
+ uint32_t alignment = 0;
+ if (flags & kAlign32) {
+ flags &= ~kAlign32;
+ alignment = 4;
+ }
+ return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
+ return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
+ uint32_t alignment = 0;
+ if (flags & kAlign32) {
+ flags &= ~kAlign32;
+ alignment = 4;
+ }
+ return StartAlignedEntryWithTime(path, flags, time, alignment);
+}
+
+static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
+ /* round up to an even number of seconds */
+ when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
+
+ struct tm* ptm;
+#if !defined(_WIN32)
+ struct tm tm_result;
+ ptm = localtime_r(&when, &tm_result);
+#else
+ ptm = localtime(&when);
+#endif
+
+ int year = ptm->tm_year;
+ if (year < 80) {
+ year = 80;
+ }
+
+ *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
+ *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
+}
+
+static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
+ LocalFileHeader* dst) {
+ dst->lfh_signature = LocalFileHeader::kSignature;
+ if (use_data_descriptor) {
+ // Set this flag to denote that a DataDescriptor struct will appear after the data,
+ // containing the crc and size fields.
+ dst->gpb_flags |= kGPBDDFlagMask;
+
+ // The size and crc fields must be 0.
+ dst->compressed_size = 0u;
+ dst->uncompressed_size = 0u;
+ dst->crc32 = 0u;
+ } else {
+ dst->compressed_size = src.compressed_size;
+ dst->uncompressed_size = src.uncompressed_size;
+ dst->crc32 = src.crc32;
+ }
+ dst->compression_method = src.compression_method;
+ dst->last_mod_time = src.last_mod_time;
+ dst->last_mod_date = src.last_mod_date;
+ DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
+ dst->file_name_length = static_cast<uint16_t>(src.path.size());
+ dst->extra_field_length = src.padding_length;
+}
+
+int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
+ uint32_t alignment) {
+ if (state_ != State::kWritingZip) {
+ return kInvalidState;
+ }
+
+ // Can only have 16535 entries because of zip records.
+ if (files_.size() == std::numeric_limits<uint16_t>::max()) {
+ return HandleError(kIoError);
+ }
+
+ if (flags & kAlign32) {
+ return kInvalidAlign32Flag;
+ }
+
+ if (powerof2(alignment) == 0) {
+ return kInvalidAlignment;
+ }
+ if (alignment > std::numeric_limits<uint16_t>::max()) {
+ return kInvalidAlignment;
+ }
+
+ FileEntry file_entry = {};
+ file_entry.local_file_header_offset = current_offset_;
+ file_entry.path = path;
+ // No support for larger than 4GB files.
+ if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+
+ if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
+ file_entry.path.size())) {
+ return kInvalidEntryName;
+ }
+
+ if (flags & ZipWriter::kCompress) {
+ file_entry.compression_method = kCompressDeflated;
+
+ int32_t result = PrepareDeflate();
+ if (result != kNoError) {
+ return result;
+ }
+ } else {
+ file_entry.compression_method = kCompressStored;
+ }
+
+ ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
+
+ off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
+ // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
+ static constexpr auto kPageSize = 4096;
+ static constexpr char kSmallZeroPadding[kPageSize] = {};
+ // use this buffer if our preallocated one is too small
+ std::vector<char> zero_padding_big;
+ const char* zero_padding = nullptr;
+
+ if (alignment != 0 && (offset & (alignment - 1))) {
+ // Pad the extra field so the data will be aligned.
+ uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
+ file_entry.padding_length = padding;
+ offset += padding;
+ if (padding <= std::size(kSmallZeroPadding)) {
+ zero_padding = kSmallZeroPadding;
+ } else {
+ zero_padding_big.resize(padding, 0);
+ zero_padding = zero_padding_big.data();
+ }
+ }
+
+ LocalFileHeader header = {};
+ // Always start expecting a data descriptor. When the data has finished being written,
+ // if it is possible to seek back, the GPB flag will reset and the sizes written.
+ CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
+
+ if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
+ return HandleError(kIoError);
+ }
+
+ if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
+ file_) != file_entry.padding_length) {
+ return HandleError(kIoError);
+ }
+
+ current_file_entry_ = std::move(file_entry);
+ current_offset_ = offset;
+ state_ = State::kWritingEntry;
+ return kNoError;
+}
+
+int32_t ZipWriter::DiscardLastEntry() {
+ if (state_ != State::kWritingZip || files_.empty()) {
+ return kInvalidState;
+ }
+
+ FileEntry& last_entry = files_.back();
+ current_offset_ = last_entry.local_file_header_offset;
+ if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
+ files_.pop_back();
+ return kNoError;
+}
+
+int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
+ CHECK(out_entry != nullptr);
+
+ if (files_.empty()) {
+ return kInvalidState;
+ }
+ *out_entry = files_.back();
+ return kNoError;
+}
+
+int32_t ZipWriter::PrepareDeflate() {
+ CHECK(state_ == State::kWritingZip);
+
+ // Initialize the z_stream for compression.
+ z_stream_ = std::unique_ptr<z_stream, void (*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+ int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+#pragma GCC diagnostic pop
+
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")";
+ return HandleError(kZlibError);
+ } else {
+ LOG(ERROR) << "deflateInit2 failed (zerr=" << zerr << ")";
+ return HandleError(kZlibError);
+ }
+ }
+
+ z_stream_->next_out = buffer_.data();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
+ return kNoError;
+}
+
+int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
+ if (state_ != State::kWritingEntry) {
+ return HandleError(kInvalidState);
+ }
+ // Need to be able to mark down data correctly.
+ if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
+ std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+ uint32_t len32 = static_cast<uint32_t>(len);
+
+ int32_t result = kNoError;
+ if (current_file_entry_.compression_method & kCompressDeflated) {
+ result = CompressBytes(&current_file_entry_, data, len32);
+ } else {
+ result = StoreBytes(&current_file_entry_, data, len32);
+ }
+
+ if (result != kNoError) {
+ return result;
+ }
+
+ current_file_entry_.crc32 = static_cast<uint32_t>(
+ crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
+ current_file_entry_.uncompressed_size += len32;
+ return kNoError;
+}
+
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
+ CHECK(state_ == State::kWritingEntry);
+
+ if (fwrite(data, 1, len, file_) != len) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += len;
+ current_offset_ += len;
+ return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
+ CHECK(state_ == State::kWritingEntry);
+ CHECK(z_stream_);
+ CHECK(z_stream_->next_out != nullptr);
+ CHECK(z_stream_->avail_out != 0);
+
+ // Prepare the input.
+ z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+ z_stream_->avail_in = len;
+
+ while (z_stream_->avail_in > 0) {
+ // We have more data to compress.
+ int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+ if (zerr != Z_OK) {
+ return HandleError(kZlibError);
+ }
+
+ if (z_stream_->avail_out == 0) {
+ // The output is full, let's write it to disk.
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+
+ // Reset the output buffer for the next input.
+ z_stream_->next_out = buffer_.data();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
+ }
+ }
+ return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
+ CHECK(state_ == State::kWritingEntry);
+ CHECK(z_stream_);
+ CHECK(z_stream_->next_out != nullptr);
+ CHECK(z_stream_->avail_out != 0);
+
+ // Keep deflating while there isn't enough space in the buffer to
+ // to complete the compress.
+ int zerr;
+ while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
+ CHECK(z_stream_->avail_out == 0);
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+
+ z_stream_->next_out = buffer_.data();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
+ }
+ if (zerr != Z_STREAM_END) {
+ return HandleError(kZlibError);
+ }
+
+ size_t write_bytes = z_stream_->next_out - buffer_.data();
+ if (write_bytes != 0) {
+ if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+ return HandleError(kIoError);
+ }
+ file->compressed_size += write_bytes;
+ current_offset_ += write_bytes;
+ }
+ z_stream_.reset();
+ return kNoError;
+}
+
+bool ZipWriter::ShouldUseDataDescriptor() const {
+ // Only use a trailing "data descriptor" if the output isn't seekable.
+ return !seekable_;
+}
+
+int32_t ZipWriter::FinishEntry() {
+ if (state_ != State::kWritingEntry) {
+ return kInvalidState;
+ }
+
+ if (current_file_entry_.compression_method & kCompressDeflated) {
+ int32_t result = FlushCompressedBytes(&current_file_entry_);
+ if (result != kNoError) {
+ return result;
+ }
+ }
+
+ if (ShouldUseDataDescriptor()) {
+ // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
+ // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
+ const uint32_t sig = DataDescriptor::kOptSignature;
+ if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ DataDescriptor dd = {};
+ dd.crc32 = current_file_entry_.crc32;
+ dd.compressed_size = current_file_entry_.compressed_size;
+ dd.uncompressed_size = current_file_entry_.uncompressed_size;
+ if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+ current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+ } else {
+ // Seek back to the header and rewrite to include the size.
+ if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
+
+ LocalFileHeader header = {};
+ CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
+
+ if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
+ }
+
+ files_.emplace_back(std::move(current_file_entry_));
+ state_ = State::kWritingZip;
+ return kNoError;
+}
+
+int32_t ZipWriter::Finish() {
+ if (state_ != State::kWritingZip) {
+ return kInvalidState;
+ }
+
+ off_t startOfCdr = current_offset_;
+ for (FileEntry& file : files_) {
+ CentralDirectoryRecord cdr = {};
+ cdr.record_signature = CentralDirectoryRecord::kSignature;
+ if (ShouldUseDataDescriptor()) {
+ cdr.gpb_flags |= kGPBDDFlagMask;
+ }
+ cdr.compression_method = file.compression_method;
+ cdr.last_mod_time = file.last_mod_time;
+ cdr.last_mod_date = file.last_mod_date;
+ cdr.crc32 = file.crc32;
+ cdr.compressed_size = file.compressed_size;
+ cdr.uncompressed_size = file.uncompressed_size;
+ // Checked in IsValidEntryName.
+ DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
+ cdr.file_name_length = static_cast<uint16_t>(file.path.size());
+ // Checked in StartAlignedEntryWithTime.
+ DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
+ cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
+ if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(cdr) + file.path.size();
+ }
+
+ EocdRecord er = {};
+ er.eocd_signature = EocdRecord::kSignature;
+ er.disk_num = 0;
+ er.cd_start_disk = 0;
+ // Checked when adding entries.
+ DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
+ er.num_records_on_disk = static_cast<uint16_t>(files_.size());
+ er.num_records = static_cast<uint16_t>(files_.size());
+ if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+ er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
+ er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
+
+ if (fwrite(&er, sizeof(er), 1, file_) != 1) {
+ return HandleError(kIoError);
+ }
+
+ current_offset_ += sizeof(er);
+
+ // Since we can BackUp() and potentially finish writing at an offset less than one we had
+ // already written at, we must truncate the file.
+
+ if (ftruncate(fileno(file_), current_offset_) != 0) {
+ return HandleError(kIoError);
+ }
+
+ if (fflush(file_) != 0) {
+ return HandleError(kIoError);
+ }
+
+ state_ = State::kDone;
+ return kNoError;
+}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
new file mode 100644
index 000000000..d324d4bfd
--- /dev/null
+++ b/libziparchive/zip_writer_test.cc
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ziparchive/zip_writer.h"
+#include "ziparchive/zip_archive.h"
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <time.h>
+#include <memory>
+#include <vector>
+
+static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
+ ZipArchiveHandle handle,
+ ZipEntry* zip_entry);
+
+struct zipwriter : public ::testing::Test {
+ TemporaryFile* temp_file_;
+ int fd_;
+ FILE* file_;
+
+ void SetUp() override {
+ temp_file_ = new TemporaryFile();
+ fd_ = temp_file_->fd;
+ file_ = fdopen(fd_, "w");
+ ASSERT_NE(file_, nullptr);
+ }
+
+ void TearDown() override {
+ fclose(file_);
+ delete temp_file_;
+ }
+};
+
+TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
+ ZipWriter writer(file_);
+
+ const char* expected = "hello";
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(0u, data.has_data_descriptor);
+ EXPECT_EQ(strlen(expected), data.compressed_length);
+ ASSERT_EQ(strlen(expected), data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(2u, data.compressed_length);
+ ASSERT_EQ(2u, data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
+
+ ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(3u, data.compressed_length);
+ ASSERT_EQ(3u, data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
+
+ ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(0u, data.compressed_length);
+ EXPECT_EQ(0u, data.uncompressed_length);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
+ EXPECT_EQ(0, data.offset & 0x03);
+
+ CloseArchive(handle);
+}
+
+static struct tm MakeTm() {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = 2001 - 1900;
+ tm.tm_mon = 1;
+ tm.tm_mday = 12;
+ tm.tm_hour = 18;
+ tm.tm_min = 30;
+ tm.tm_sec = 20;
+ return tm;
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
+ ZipWriter writer(file_);
+
+ struct tm tm = MakeTm();
+ time_t time = mktime(&tm);
+ ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
+ EXPECT_EQ(0, data.offset & 0x03);
+
+ struct tm mod = data.GetModificationTime();
+ EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+ EXPECT_EQ(tm.tm_min, mod.tm_min);
+ EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+ EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+ EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+ EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
+ EXPECT_EQ(0, data.offset & 0xfff);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
+ ZipWriter writer(file_);
+
+ struct tm tm = MakeTm();
+ time_t time = mktime(&tm);
+ ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
+ EXPECT_EQ(0, data.offset & 0xfff);
+
+ struct tm mod = data.GetModificationTime();
+ EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+ EXPECT_EQ(tm.tm_min, mod.tm_min);
+ EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+ EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+ EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+ EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+ EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(0u, data.has_data_descriptor);
+ ASSERT_EQ(4u, data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipFlushFull) {
+ // This exact data will cause the Finish() to require multiple calls
+ // to deflate() because the ZipWriter buffer isn't big enough to hold
+ // the entire compressed data buffer.
+ constexpr size_t kBufSize = 10000000;
+ std::vector<uint8_t> buffer(kBufSize);
+ size_t prev = 1;
+ for (size_t i = 0; i < kBufSize; i++) {
+ buffer[i] = static_cast<uint8_t>(i + prev);
+ prev = i;
+ }
+
+ ZipWriter writer(file_);
+ ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+ ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+ EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(kBufSize, data.uncompressed_length);
+
+ std::vector<uint8_t> decompress(kBufSize);
+ memset(decompress.data(), 0, kBufSize);
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
+ static_cast<uint32_t>(decompress.size())));
+ EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
+ << "Input buffer and output buffer are different.";
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, CheckStartEntryErrors) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
+ ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
+}
+
+TEST_F(zipwriter, BackupRemovesTheLastFile) {
+ ZipWriter writer(file_);
+
+ const char* kKeepThis = "keep this";
+ const char* kDropThis = "drop this";
+ const char* kReplaceWithThis = "replace with this";
+
+ ZipWriter::FileEntry entry;
+ EXPECT_LT(writer.GetLastEntry(&entry), 0);
+
+ ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("keep.txt", entry.path);
+
+ ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("drop.txt", entry.path);
+
+ ASSERT_EQ(0, writer.DiscardLastEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("keep.txt", entry.path);
+
+ ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("replace.txt", entry.path);
+
+ ASSERT_EQ(0, writer.Finish());
+
+ // Verify that "drop.txt" does not exist.
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
+ ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
+
+ ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
+
+ ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
+ ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteToUnseekableFile) {
+ const char* expected = "hello";
+ ZipWriter writer(file_);
+ writer.seekable_ = false;
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(1u, data.has_data_descriptor);
+ EXPECT_EQ(strlen(expected), data.compressed_length);
+ ASSERT_EQ(strlen(expected), data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, TruncateFileAfterBackup) {
+ ZipWriter writer(file_);
+
+ const char* kSmall = "small";
+
+ ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
+ std::vector<uint8_t> data;
+ data.resize(1024 * 1024, 0xef);
+ ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ off_t before_len = ftello(file_);
+
+ ZipWriter::FileEntry entry;
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ ASSERT_EQ(0, writer.DiscardLastEntry());
+
+ ASSERT_EQ(0, writer.Finish());
+
+ off_t after_len = ftello(file_);
+
+ ASSERT_GT(before_len, after_len);
+}
+
+static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
+ ZipArchiveHandle handle,
+ ZipEntry* zip_entry) {
+ if (expected.size() != zip_entry->uncompressed_length) {
+ return ::testing::AssertionFailure()
+ << "uncompressed entry size " << zip_entry->uncompressed_length
+ << " does not match expected size " << expected.size();
+ }
+
+ std::string actual;
+ actual.resize(expected.size());
+
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
+ if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
+ return ::testing::AssertionFailure() << "failed to extract entry";
+ }
+
+ if (expected != actual) {
+ return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
+ << "' does not match expected '" << expected << "'";
+ }
+ return ::testing::AssertionSuccess();
+}
diff --git a/libziparchive/ziptool-tests.xml b/libziparchive/ziptool-tests.xml
new file mode 100644
index 000000000..211119fe3
--- /dev/null
+++ b/libziparchive/ziptool-tests.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for running ziptool-tests through Atest or in Infra">
+ <option name="test-suite-tag" value="ziptool-tests" />
+ <!-- This test requires a device, so it's not annotated with a null-device. -->
+ <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+ <option name="binary" value="run-ziptool-tests-on-android.sh" />
+ <!-- Test script assumes a relative path with the cli-tests/ folders. -->
+ <option name="relative-path-execution" value="true" />
+ <!-- Tests shouldn't be that long but set 15m to be safe. -->
+ <option name="per-binary-timeout" value="15m" />
+ </test>
+</configuration>
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
new file mode 100644
index 000000000..dd42e90b9
--- /dev/null
+++ b/libziparchive/ziptool.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <ziparchive/zip_archive.h>
+
+using android::base::EndsWith;
+using android::base::StartsWith;
+
+enum OverwriteMode {
+ kAlways,
+ kNever,
+ kPrompt,
+};
+
+enum Role {
+ kUnzip,
+ kZipinfo,
+};
+
+static Role role;
+static OverwriteMode overwrite_mode = kPrompt;
+static bool flag_1 = false;
+static std::string flag_d;
+static bool flag_l = false;
+static bool flag_p = false;
+static bool flag_q = false;
+static bool flag_v = false;
+static bool flag_x = false;
+static const char* archive_name = nullptr;
+static std::set<std::string> includes;
+static std::set<std::string> excludes;
+static uint64_t total_uncompressed_length = 0;
+static uint64_t total_compressed_length = 0;
+static size_t file_count = 0;
+
+static const char* g_progname;
+
+static void die(int error, const char* fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: ", g_progname);
+ vfprintf(stderr, fmt, ap);
+ if (error != 0) fprintf(stderr, ": %s", strerror(error));
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+static bool ShouldInclude(const std::string& name) {
+ // Explicitly excluded?
+ if (!excludes.empty()) {
+ for (const auto& exclude : excludes) {
+ if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
+ }
+ }
+
+ // Implicitly included?
+ if (includes.empty()) return true;
+
+ // Explicitly included?
+ for (const auto& include : includes) {
+ if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
+ }
+ return false;
+}
+
+static bool MakeDirectoryHierarchy(const std::string& path) {
+ // stat rather than lstat because a symbolic link to a directory is fine too.
+ struct stat sb;
+ if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return true;
+
+ // Ensure the parent directories exist first.
+ if (!MakeDirectoryHierarchy(android::base::Dirname(path))) return false;
+
+ // Then try to create this directory.
+ return (mkdir(path.c_str(), 0777) != -1);
+}
+
+static float CompressionRatio(int64_t uncompressed, int64_t compressed) {
+ if (uncompressed == 0) return 0;
+ return static_cast<float>(100LL * (uncompressed - compressed)) /
+ static_cast<float>(uncompressed);
+}
+
+static void MaybeShowHeader(ZipArchiveHandle zah) {
+ if (role == kUnzip) {
+ // unzip has three formats.
+ if (!flag_q) printf("Archive: %s\n", archive_name);
+ if (flag_v) {
+ printf(
+ " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ "-------- ------ ------- ---- ---------- ----- -------- ----\n");
+ } else if (flag_l) {
+ printf(
+ " Length Date Time Name\n"
+ "--------- ---------- ----- ----\n");
+ }
+ } else {
+ // zipinfo.
+ if (!flag_1 && includes.empty() && excludes.empty()) {
+ ZipArchiveInfo info{GetArchiveInfo(zah)};
+ printf("Archive: %s\n", archive_name);
+ printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
+ info.entry_count);
+ }
+ }
+}
+
+static void MaybeShowFooter() {
+ if (role == kUnzip) {
+ if (flag_v) {
+ printf(
+ "-------- ------- --- -------\n"
+ "%8" PRId64 " %8" PRId64 " %3.0f%% %zu file%s\n",
+ total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+ (file_count == 1) ? "" : "s");
+ } else if (flag_l) {
+ printf(
+ "--------- -------\n"
+ "%9" PRId64 " %zu file%s\n",
+ total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+ }
+ } else {
+ if (!flag_1 && includes.empty() && excludes.empty()) {
+ printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %.1f%%\n",
+ file_count, total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length));
+ }
+ }
+}
+
+static bool PromptOverwrite(const std::string& dst) {
+ // TODO: [r]ename not implemented because it doesn't seem useful.
+ printf("replace %s? [y]es, [n]o, [A]ll, [N]one: ", dst.c_str());
+ fflush(stdout);
+ while (true) {
+ char* line = nullptr;
+ size_t n;
+ if (getline(&line, &n, stdin) == -1) {
+ die(0, "(EOF/read error; assuming [N]one...)");
+ overwrite_mode = kNever;
+ return false;
+ }
+ if (n == 0) continue;
+ char cmd = line[0];
+ free(line);
+ switch (cmd) {
+ case 'y':
+ return true;
+ case 'n':
+ return false;
+ case 'A':
+ overwrite_mode = kAlways;
+ return true;
+ case 'N':
+ overwrite_mode = kNever;
+ return false;
+ }
+ }
+}
+
+static void ExtractToPipe(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ // We need to extract to memory because ExtractEntryToFile insists on
+ // being able to seek and truncate, and you can't do that with stdout.
+ uint8_t* buffer = new uint8_t[entry.uncompressed_length];
+ int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
+ if (err < 0) {
+ die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+ }
+ if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
+ die(errno, "failed to write %s to stdout", name.c_str());
+ }
+ delete[] buffer;
+}
+
+static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ // Bad filename?
+ if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
+ die(0, "bad filename %s", name.c_str());
+ }
+
+ // Where are we actually extracting to (for human-readable output)?
+ // flag_d is the empty string if -d wasn't used, or has a trailing '/'
+ // otherwise.
+ std::string dst = flag_d + name;
+
+ // Ensure the directory hierarchy exists.
+ if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
+ die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
+ }
+
+ // An entry in a zip file can just be a directory itself.
+ if (EndsWith(name, "/")) {
+ if (mkdir(name.c_str(), entry.unix_mode) == -1) {
+ // If the directory already exists, that's fine.
+ if (errno == EEXIST) {
+ struct stat sb;
+ if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
+ }
+ die(errno, "couldn't extract directory %s", dst.c_str());
+ }
+ return;
+ }
+
+ // Create the file.
+ int fd = open(name.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL, entry.unix_mode);
+ if (fd == -1 && errno == EEXIST) {
+ if (overwrite_mode == kNever) return;
+ if (overwrite_mode == kPrompt && !PromptOverwrite(dst)) return;
+ // Either overwrite_mode is kAlways or the user consented to this specific case.
+ fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
+ }
+ if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
+
+ // Actually extract into the file.
+ if (!flag_q) printf(" inflating: %s\n", dst.c_str());
+ int err = ExtractEntryToFile(zah, &entry, fd);
+ if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+ close(fd);
+}
+
+static void ListOne(const ZipEntry& entry, const std::string& name) {
+ tm t = entry.GetModificationTime();
+ char time[32];
+ snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+ t.tm_mday, t.tm_hour, t.tm_min);
+ if (flag_v) {
+ printf("%8d %s %7d %3.0f%% %s %08x %s\n", entry.uncompressed_length,
+ (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
+ CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
+ name.c_str());
+ } else {
+ printf("%9d %s %s\n", entry.uncompressed_length, time, name.c_str());
+ }
+}
+
+static void InfoOne(const ZipEntry& entry, const std::string& name) {
+ if (flag_1) {
+ // "android-ndk-r19b/sources/android/NOTICE"
+ printf("%s\n", name.c_str());
+ return;
+ }
+
+ int version = entry.version_made_by & 0xff;
+ int os = (entry.version_made_by >> 8) & 0xff;
+
+ // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
+ const char* src_fs = "???";
+ char mode[] = "??? ";
+ if (os == 0) {
+ src_fs = "fat";
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+ int attrs = entry.external_file_attributes & 0xff;
+ mode[0] = (attrs & 0x10) ? 'd' : '-';
+ mode[1] = 'r';
+ mode[2] = (attrs & 0x01) ? '-' : 'w';
+ // The man page also mentions ".btm", but that seems to be obsolete?
+ mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
+ EndsWith(name, ".cmd")
+ ? 'x'
+ : '-';
+ mode[4] = (attrs & 0x20) ? 'a' : '-';
+ mode[5] = (attrs & 0x02) ? 'h' : '-';
+ mode[6] = (attrs & 0x04) ? 's' : '-';
+ } else if (os == 3) {
+ src_fs = "unx";
+ mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
+ mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
+ mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
+ mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
+ mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
+ mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
+ mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
+ mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
+ mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
+ mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
+ }
+
+ char method[5] = "stor";
+ if (entry.method == kCompressDeflated) {
+ snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
+ }
+
+ // TODO: zipinfo (unlike unzip) sometimes uses time zone?
+ // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
+ tm t = entry.GetModificationTime();
+ char time[32];
+ snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+ t.tm_mday, t.tm_hour, t.tm_min);
+
+ // "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
+ printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
+ entry.uncompressed_length, entry.is_text ? 't' : 'b',
+ entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
+}
+
+static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
+ if (role == kUnzip) {
+ if (flag_l || flag_v) {
+ // -l or -lv or -lq or -v.
+ ListOne(entry, name);
+ } else {
+ // Actually extract.
+ if (flag_p) {
+ ExtractToPipe(zah, entry, name);
+ } else {
+ ExtractOne(zah, entry, name);
+ }
+ }
+ } else {
+ // zipinfo or zipinfo -1.
+ InfoOne(entry, name);
+ }
+ total_uncompressed_length += entry.uncompressed_length;
+ total_compressed_length += entry.compressed_length;
+ ++file_count;
+}
+
+static void ProcessAll(ZipArchiveHandle zah) {
+ MaybeShowHeader(zah);
+
+ // libziparchive iteration order doesn't match the central directory.
+ // We could sort, but that would cost extra and wouldn't match either.
+ void* cookie;
+ int err = StartIteration(zah, &cookie);
+ if (err != 0) {
+ die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+ }
+
+ ZipEntry entry;
+ std::string name;
+ while ((err = Next(cookie, &entry, &name)) >= 0) {
+ if (ShouldInclude(name)) ProcessOne(zah, entry, name);
+ }
+
+ if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+ EndIteration(cookie);
+
+ MaybeShowFooter();
+}
+
+static void ShowHelp(bool full) {
+ if (role == kUnzip) {
+ fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
+
+ printf(
+ "\n"
+ "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+ "exclude (-x) lists use shell glob patterns.\n"
+ "\n"
+ "-d DIR Extract into DIR\n"
+ "-l List contents (-lq excludes archive name, -lv is verbose)\n"
+ "-n Never overwrite files (default: prompt)\n"
+ "-o Always overwrite files\n"
+ "-p Pipe to stdout\n"
+ "-q Quiet\n"
+ "-v List contents verbosely\n"
+ "-x FILE Exclude files\n");
+ } else {
+ fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
+
+ printf(
+ "\n"
+ "Show information about FILEs from ZIP archive. Default is all files.\n"
+ "Both the include and exclude (-x) lists use shell glob patterns.\n"
+ "\n"
+ "-1 Show filenames only, one per line\n"
+ "-x FILE Exclude files\n");
+ }
+ exit(EXIT_SUCCESS);
+}
+
+static void HandleCommonOption(int opt) {
+ switch (opt) {
+ case 'h':
+ ShowHelp(true);
+ break;
+ case 'x':
+ flag_x = true;
+ break;
+ case 1:
+ // -x swallows all following arguments, so we use '-' in the getopt
+ // string and collect files here.
+ if (!archive_name) {
+ archive_name = optarg;
+ } else if (flag_x) {
+ excludes.insert(optarg);
+ } else {
+ includes.insert(optarg);
+ }
+ break;
+ default:
+ ShowHelp(false);
+ break;
+ }
+}
+
+int main(int argc, char* argv[]) {
+ // Who am I, and what am I doing?
+ g_progname = basename(argv[0]);
+ if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
+ if (!strcmp(g_progname, "unzip")) {
+ role = kUnzip;
+ } else if (!strcmp(g_progname, "zipinfo")) {
+ role = kZipinfo;
+ } else {
+ die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
+ }
+
+ static const struct option opts[] = {
+ {"help", no_argument, 0, 'h'},
+ {},
+ };
+
+ if (role == kUnzip) {
+ // `unzip -Z` is "zipinfo mode", so in that case just restart...
+ if (argc > 1 && !strcmp(argv[1], "-Z")) {
+ argv[1] = const_cast<char*>("zipinfo");
+ return main(argc - 1, argv + 1);
+ }
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case 'd':
+ flag_d = optarg;
+ if (!EndsWith(flag_d, "/")) flag_d += '/';
+ break;
+ case 'l':
+ flag_l = true;
+ break;
+ case 'n':
+ overwrite_mode = kNever;
+ break;
+ case 'o':
+ overwrite_mode = kAlways;
+ break;
+ case 'p':
+ flag_p = flag_q = true;
+ break;
+ case 'q':
+ flag_q = true;
+ break;
+ case 'v':
+ flag_v = true;
+ break;
+ default:
+ HandleCommonOption(opt);
+ break;
+ }
+ }
+ } else {
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case '1':
+ flag_1 = true;
+ break;
+ default:
+ HandleCommonOption(opt);
+ break;
+ }
+ }
+ }
+
+ if (!archive_name) die(0, "missing archive filename");
+
+ // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
+ ZipArchiveHandle zah;
+ int32_t err;
+ if ((err = OpenArchive(archive_name, &zah)) != 0) {
+ die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+ }
+
+ // Implement -d by changing into that directory.
+ // We'll create implicit directories based on paths in the zip file, and we'll create
+ // the -d directory itself, but we require that *parents* of the -d directory already exists.
+ // This is pretty arbitrary, but it's the behavior of the original unzip.
+ if (!flag_d.empty()) {
+ if (mkdir(flag_d.c_str(), 0777) == -1 && errno != EEXIST) {
+ die(errno, "couldn't created %s", flag_d.c_str());
+ }
+ if (chdir(flag_d.c_str()) == -1) {
+ die(errno, "couldn't chdir to %s", flag_d.c_str());
+ }
+ }
+
+ ProcessAll(zah);
+
+ CloseArchive(zah);
+ return 0;
+}
diff --git a/llkd/Android.bp b/llkd/Android.bp
index 1c0e0f0a0..62a637dcd 100644
--- a/llkd/Android.bp
+++ b/llkd/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "llkd_headers",
diff --git a/llkd/README.md b/llkd/README.md
index 9bcf806b5..6f92f1474 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -207,7 +207,7 @@ Comma-separated list of uid numbers or names. Default is empty or false.
The `llkd` does not monitor the specified subset of processes for live lock stack
signatures. Default is process names
-`init,lmkd.llkd,llkd,keystore,keystore2,ueventd,apexd,logd`. Prevents the sepolicy
+`init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd`. Prevents the sepolicy
violation associated with processes that block `ptrace` (as these can't be
checked). **Active only on userdebug and eng builds**. For details on build
types, refer to [Building Android](/setup/build/building#choose-a-target).
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 0822a3e2b..4b20a56da 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -60,7 +60,7 @@ unsigned llkCheckMilliseconds(void);
#define LLK_IGNORELIST_UID_PROPERTY "ro.llk.ignorelist.uid"
#define LLK_IGNORELIST_UID_DEFAULT ""
#define LLK_IGNORELIST_STACK_PROPERTY "ro.llk.ignorelist.process.stack"
-#define LLK_IGNORELIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,keystore2,ueventd,apexd"
+#define LLK_IGNORELIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
/* clang-format on */
__END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index c4c58eef3..a24d900dc 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -115,8 +115,8 @@ std::unordered_map<std::string, std::unordered_set<std::string>> llkIgnorelistPa
// list of uids, and uid names, to skip, default nothing
std::unordered_set<std::string> llkIgnorelistUid;
#ifdef __PTRACE_ENABLED__
-// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore",
-// "keystore2", or "logd" (if not userdebug).
+// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore" or
+// "logd" (if not userdebug).
std::unordered_set<std::string> llkIgnorelistStack;
#endif
@@ -962,8 +962,7 @@ milliseconds llkCheck(bool checkRunning) {
//
// This alarm is effectively the live lock detection of llkd, as
// we understandably can not monitor ourselves otherwise.
- ::alarm(duration_cast<seconds>(llkTimeoutMs * 2 * android::base::HwTimeoutMultiplier())
- .count());
+ ::alarm(duration_cast<seconds>(llkTimeoutMs * 2).count());
// kernel jiffy precision fastest acquisition
static timespec last;
diff --git a/llkd/tests/Android.bp b/llkd/tests/Android.bp
index 3db6e85fd..6dd5938e3 100644
--- a/llkd/tests/Android.bp
+++ b/llkd/tests/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_test {
name: "llkd_unit_test",
diff --git a/logcat/Android.bp b/logcat/Android.bp
new file mode 100644
index 000000000..61fba5928
--- /dev/null
+++ b/logcat/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2006 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "logcat_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
+ ],
+ shared_libs: [
+ "libbase",
+ "libprocessgroup",
+ ],
+ static_libs: ["liblog"],
+ logtags: ["event.logtags"],
+}
+
+cc_binary {
+ name: "logcat",
+
+ defaults: ["logcat_defaults"],
+ srcs: [
+ "logcat.cpp",
+ ],
+}
+
+sh_binary {
+ name: "logcatd",
+ src: "logcatd",
+}
+
+sh_binary {
+ name: "logpersist.start",
+ src: "logpersist",
+ init_rc: ["logcatd.rc"],
+ required: ["logcatd"],
+ symlinks: [
+ "logpersist.stop",
+ "logpersist.cat",
+ ],
+}
diff --git a/healthd/testdata/legacy/res/images/charger/battery_fail.png b/logcat/MODULE_LICENSE_APACHE2
index e69de29bb..e69de29bb 100644
--- a/healthd/testdata/legacy/res/images/charger/battery_fail.png
+++ b/logcat/MODULE_LICENSE_APACHE2
diff --git a/logcat/NOTICE b/logcat/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/logcat/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/logcat/OWNERS b/logcat/OWNERS
new file mode 100644
index 000000000..babbe4ded
--- /dev/null
+++ b/logcat/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/logcat/event.logtags b/logcat/event.logtags
new file mode 100644
index 000000000..56a670a17
--- /dev/null
+++ b/logcat/event.logtags
@@ -0,0 +1,156 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 5: float
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# s: Number of seconds (monotonic time)
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+# These are used for testing, do not modify without updating
+# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+# system/core/liblog/tests/liblog_benchmark.cpp
+# system/core/liblog/tests/liblog_test.cpp
+42 answer (to life the universe etc|3)
+314 pi
+2718 e
+
+# "account" is the java hash of the account name
+2720 sync (id|3),(event|1|5),(source|1|5),(account|1|5)
+
+# This event is logged when the location service uploads location data.
+2740 location_controller
+# This event is logged when someone is deciding to force a garbage collection
+2741 force_gc (reason|3)
+# This event is logged on each tickle
+2742 tickle (authority|3)
+
+# contacts aggregation: time and number of contacts.
+# count is negative for query phase, positive for merge phase
+2747 contacts_aggregation (aggregation time|2|3), (count|1|1)
+
+# Device boot timings. We include monotonic clock values because the
+# intrinsic event log times are wall-clock.
+#
+# Runtime starts:
+3000 boot_progress_start (time|2|3)
+# ZygoteInit class preloading starts:
+3020 boot_progress_preload_start (time|2|3)
+# ZygoteInit class preloading ends:
+3030 boot_progress_preload_end (time|2|3)
+
+# Dalvik VM / ART
+20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
+20004 art_hidden_api_access (access_method|1),(flags|1),(class|3),(member|3),(type_signature|3)
+
+75000 sqlite_mem_alarm_current (current|1|2)
+75001 sqlite_mem_alarm_max (max|1|2)
+75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4)
+75003 sqlite_mem_released (Memory released|1|2)
+75004 sqlite_db_corrupt (Database file corrupt|3)
+
+50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3)
+50001 menu_opened (Menu type where 0 is options and 1 is context|1|5)
+
+# HSM wifi state change
+# Hierarchical state class name (as defined in WifiStateTracker.java)
+# Logged on every state change in the hierarchical state machine
+50021 wifi_state_changed (wifi_state|3)
+# HSM wifi event
+# [31-16] Reserved for future use
+# [15 - 0] HSM event (as defined in WifiStateTracker.java)
+# Logged when an event is handled in a hierarchical state
+50022 wifi_event_handled (wifi_event|1|5)
+# Supplicant state change
+# [31-13] Reserved for future use
+# [8 - 0] Supplicant state (as defined in SupplicantState.java)
+# Logged when the supplicant switches to a new state
+50023 wifi_supplicant_state_changed (supplicant_state|1|5)
+
+# Database operation samples.
+# db: the filename of the database
+# sql: the executed query (without query args)
+# time: cpu time millis (not wall time), including lock acquisition
+# blocking_package: if this is on a main thread, the package name, otherwise ""
+# sample_percent: the percent likelihood this query was logged
+52000 db_sample (db|3),(sql|3),(time|1|3),(blocking_package|3),(sample_percent|1|6)
+
+# http request/response stats
+52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2)
+60000 viewroot_draw (Draw time|1|3)
+60001 viewroot_layout (Layout time|1|3)
+60002 view_build_drawing_cache (View created drawing cache|1|5)
+60003 view_use_drawing_cache (View drawn using bitmap cache|1|5)
+
+# graphics timestamp
+# 60100 - 60199 reserved for surfaceflinger
+
+# audio
+# 61000 - 61199 reserved for audioserver
+
+# com.android.server.policy
+# 70000 - 70199 reserved for PhoneWindowManager and other policies
+
+# aggregation service
+70200 aggregation (aggregation time|2|3)
+70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2)
+
+# gms refuses to register this log tag, b/30156345
+70220 gms_unknown
+
+# libc failure logging
+80100 bionic_event_memcpy_buffer_overflow (uid|1)
+80105 bionic_event_strcat_buffer_overflow (uid|1)
+80110 bionic_event_memmov_buffer_overflow (uid|1)
+80115 bionic_event_strncat_buffer_overflow (uid|1)
+80120 bionic_event_strncpy_buffer_overflow (uid|1)
+80125 bionic_event_memset_buffer_overflow (uid|1)
+80130 bionic_event_strcpy_buffer_overflow (uid|1)
+
+80200 bionic_event_strcat_integer_overflow (uid|1)
+80205 bionic_event_strncat_integer_overflow (uid|1)
+
+80300 bionic_event_resolver_old_response (uid|1)
+80305 bionic_event_resolver_wrong_server (uid|1)
+80310 bionic_event_resolver_wrong_query (uid|1)
+
+# libcore failure logging
+90100 exp_det_cert_pin_failure (certs|4)
+
+# 150000 - 160000 reserved for Android Automotive builds
+
+1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
+
+# for events that go to stats log buffer
+1937006964 stats_log (atom_id|1|5),(data|4)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
new file mode 100644
index 000000000..b8c143d98
--- /dev/null
+++ b/logcat/logcat.cpp
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (C) 2006-2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <math.h>
+#include <sched.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <memory>
+#include <regex>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/log.h>
+#include <log/event_tag_map.h>
+#include <log/log_id.h>
+#include <log/logprint.h>
+#include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
+#include <system/thread_defs.h>
+
+#define DEFAULT_MAX_ROTATED_LOGS 4
+
+using android::base::Join;
+using android::base::ParseByteCount;
+using android::base::ParseUint;
+using android::base::Split;
+using android::base::StringPrintf;
+
+class Logcat {
+ public:
+ int Run(int argc, char** argv);
+
+ private:
+ void RotateLogs();
+ void ProcessBuffer(struct log_msg* buf);
+ void PrintDividers(log_id_t log_id, bool print_dividers);
+ void SetupOutputAndSchedulingPolicy(bool blocking);
+ int SetLogFormat(const char* format_string);
+
+ // Used for all options
+ android::base::unique_fd output_fd_{dup(STDOUT_FILENO)};
+ std::unique_ptr<AndroidLogFormat, decltype(&android_log_format_free)> logformat_{
+ android_log_format_new(), &android_log_format_free};
+
+ // For logging to a file and log rotation
+ const char* output_file_name_ = nullptr;
+ size_t log_rotate_size_kb_ = 0; // 0 means "no log rotation"
+ size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
+ size_t out_byte_count_ = 0;
+
+ // For binary log buffers
+ int print_binary_ = 0;
+ std::unique_ptr<EventTagMap, decltype(&android_closeEventTagMap)> event_tag_map_{
+ nullptr, &android_closeEventTagMap};
+ bool has_opened_event_tag_map_ = false;
+
+ // For the related --regex, --max-count, --print
+ std::unique_ptr<std::regex> regex_;
+ size_t max_count_ = 0; // 0 means "infinite"
+ size_t print_count_ = 0;
+ bool print_it_anyways_ = false;
+
+ // For PrintDividers()
+ log_id_t last_printed_id_ = LOG_ID_MAX;
+ bool printed_start_[LOG_ID_MAX] = {};
+
+ bool debug_ = false;
+};
+
+#ifndef F2FS_IOC_SET_PIN_FILE
+#define F2FS_IOCTL_MAGIC 0xf5
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#endif
+
+static int openLogFile(const char* pathname, size_t sizeKB) {
+ int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP);
+ if (fd < 0) {
+ return fd;
+ }
+
+ // no need to check errors
+ __u32 set = 1;
+ ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+ fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, (sizeKB << 10));
+ return fd;
+}
+
+static void closeLogFile(const char* pathname) {
+ int fd = open(pathname, O_WRONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return;
+ }
+
+ // no need to check errors
+ __u32 set = 0;
+ ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+ close(fd);
+}
+
+void Logcat::RotateLogs() {
+ // Can't rotate logs if we're not outputting to a file
+ if (!output_file_name_) return;
+
+ output_fd_.reset();
+
+ // Compute the maximum number of digits needed to count up to
+ // maxRotatedLogs in decimal. eg:
+ // maxRotatedLogs == 30
+ // -> log10(30) == 1.477
+ // -> maxRotationCountDigits == 2
+ int max_rotation_count_digits =
+ max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
+
+ for (int i = max_rotated_logs_; i > 0; i--) {
+ std::string file1 =
+ StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
+
+ std::string file0;
+ if (!(i - 1)) {
+ file0 = output_file_name_;
+ } else {
+ file0 = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i - 1);
+ }
+
+ if (!file0.length() || !file1.length()) {
+ perror("while rotating log files");
+ break;
+ }
+
+ closeLogFile(file0.c_str());
+
+ int err = rename(file0.c_str(), file1.c_str());
+
+ if (err < 0 && errno != ENOENT) {
+ perror("while rotating log files");
+ }
+ }
+
+ output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
+
+ if (!output_fd_.ok()) {
+ error(EXIT_FAILURE, errno, "Couldn't open output file");
+ }
+
+ out_byte_count_ = 0;
+}
+
+void Logcat::ProcessBuffer(struct log_msg* buf) {
+ int bytesWritten = 0;
+ int err;
+ AndroidLogEntry entry;
+ char binaryMsgBuf[1024];
+
+ bool is_binary =
+ buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
+
+ if (is_binary) {
+ if (!event_tag_map_ && !has_opened_event_tag_map_) {
+ event_tag_map_.reset(android_openEventTagMap(nullptr));
+ has_opened_event_tag_map_ = true;
+ }
+ err = android_log_processBinaryLogBuffer(&buf->entry, &entry, event_tag_map_.get(),
+ binaryMsgBuf, sizeof(binaryMsgBuf));
+ // printf(">>> pri=%d len=%d msg='%s'\n",
+ // entry.priority, entry.messageLen, entry.message);
+ } else {
+ err = android_log_processLogBuffer(&buf->entry, &entry);
+ }
+ if (err < 0 && !debug_) return;
+
+ if (android_log_shouldPrintLine(logformat_.get(), std::string(entry.tag, entry.tagLen).c_str(),
+ entry.priority)) {
+ bool match = !regex_ ||
+ std::regex_search(entry.message, entry.message + entry.messageLen, *regex_);
+
+ print_count_ += match;
+ if (match || print_it_anyways_) {
+ bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
+
+ if (bytesWritten < 0) {
+ error(EXIT_FAILURE, 0, "Output error.");
+ }
+ }
+ }
+
+ out_byte_count_ += bytesWritten;
+
+ if (log_rotate_size_kb_ > 0 && (out_byte_count_ / 1024) >= log_rotate_size_kb_) {
+ RotateLogs();
+ }
+}
+
+void Logcat::PrintDividers(log_id_t log_id, bool print_dividers) {
+ if (log_id == last_printed_id_ || print_binary_) {
+ return;
+ }
+ if (!printed_start_[log_id] || print_dividers) {
+ if (dprintf(output_fd_.get(), "--------- %s %s\n",
+ printed_start_[log_id] ? "switch to" : "beginning of",
+ android_log_id_to_name(log_id)) < 0) {
+ error(EXIT_FAILURE, errno, "Output error");
+ }
+ }
+ last_printed_id_ = log_id;
+ printed_start_[log_id] = true;
+}
+
+void Logcat::SetupOutputAndSchedulingPolicy(bool blocking) {
+ if (!output_file_name_) return;
+
+ if (blocking) {
+ // Lower priority and set to batch scheduling if we are saving
+ // the logs into files and taking continuous content.
+ if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+ fprintf(stderr, "failed to set background scheduling policy\n");
+ }
+
+ struct sched_param param = {};
+ if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
+ fprintf(stderr, "failed to set to batch scheduler\n");
+ }
+
+ if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+ fprintf(stderr, "failed set to priority\n");
+ }
+ }
+
+ output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
+
+ if (!output_fd_.ok()) {
+ error(EXIT_FAILURE, errno, "Couldn't open output file");
+ }
+
+ struct stat statbuf;
+ if (fstat(output_fd_.get(), &statbuf) == -1) {
+ error(EXIT_FAILURE, errno, "Couldn't get output file stat");
+ }
+
+ if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+ error(EXIT_FAILURE, 0, "Invalid output file stat.");
+ }
+
+ out_byte_count_ = statbuf.st_size;
+}
+
+// clang-format off
+static void show_help() {
+ const char* cmd = getprogname();
+
+ fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd);
+
+ fprintf(stderr, R"init(
+General options:
+ -b, --buffer=<buffer> Request alternate ring buffer(s):
+ main system radio events crash default all
+ Additionally, 'kernel' for userdebug and eng builds, and
+ 'security' for Device Owner installations.
+ Multiple -b parameters or comma separated list of buffers are
+ allowed. Buffers are interleaved.
+ Default -b main,system,crash,kernel.
+ -L, --last Dump logs from prior to last reboot from pstore.
+ -c, --clear Clear (flush) the entire log and exit.
+ if -f is specified, clear the specified file and its related rotated
+ log files instead.
+ if -L is specified, clear pstore log instead.
+ -d Dump the log and then exit (don't block).
+ --pid=<pid> Only print logs from the given pid.
+ --wrap Sleep for 2 hours or when buffer about to wrap whichever
+ comes first. Improves efficiency of polling by providing
+ an about-to-wrap wakeup.
+
+Formatting:
+ -v, --format=<format> Sets log print format verb and adverbs, where <format> is one of:
+ brief help long process raw tag thread threadtime time
+ Modifying adverbs can be added:
+ color descriptive epoch monotonic printable uid usec UTC year zone
+ Multiple -v parameters or comma separated list of format and format
+ modifiers are allowed.
+ -D, --dividers Print dividers between each log buffer.
+ -B, --binary Output the log in binary.
+
+Outfile files:
+ -f, --file=<file> Log to file instead of stdout.
+ -r, --rotate-kbytes=<n> Rotate log every <n> kbytes. Requires -f option.
+ -n, --rotate-count=<count> Sets max number of rotated logs to <count>, default 4.
+ --id=<id> If the signature <id> for logging to file changes, then clear the
+ associated files and continue.
+
+Logd control:
+ These options send a control message to the logd daemon on device, print its return message if
+ applicable, then exit. They are incompatible with -L, as these attributes do not apply to pstore.
+ -g, --buffer-size Get the size of the ring buffers within logd.
+ -G, --buffer-size=<size> Set size of a ring buffer in logd. May suffix with K or M.
+ This can individually control each buffer's size with -b.
+ -S, --statistics Output statistics.
+ --pid can be used to provide pid specific stats.
+ -p, --prune Print prune white and ~black list. Service is specified as UID,
+ UID/PID or /PID. Weighed for quicker pruning if prefix with ~,
+ otherwise weighed for longevity if unadorned. All other pruning
+ activity is oldest first. Special case ~! represents an automatic
+ quicker pruning for the noisiest UID as determined by the current
+ statistics.
+ -P, --prune='<list> ...' Set prune white and ~black list, using same format as listed above.
+ Must be quoted.
+
+Filtering:
+ -s Set default filter to silent. Equivalent to filterspec '*:S'
+ -e, --regex=<expr> Only print lines where the log message matches <expr> where <expr> is
+ an ECMAScript regular expression.
+ -m, --max-count=<count> Quit after printing <count> lines. This is meant to be paired with
+ --regex, but will work on its own.
+ --print This option is only applicable when --regex is set and only useful if
+ --max-count is also provided.
+ With --print, logcat will print all messages even if they do not
+ match the regex. Logcat will quit after printing the max-count number
+ of lines that match the regex.
+ -t <count> Print only the most recent <count> lines (implies -d).
+ -t '<time>' Print the lines since specified time (implies -d).
+ -T <count> Print only the most recent <count> lines (does not imply -d).
+ -T '<time>' Print the lines since specified time (not imply -d).
+ count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
+ 'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format.
+)init");
+
+ fprintf(stderr, "\nfilterspecs are a series of \n"
+ " <tag>[:priority]\n\n"
+ "where <tag> is a log component tag (or * for all) and priority is:\n"
+ " V Verbose (default for <tag>)\n"
+ " D Debug (default for '*')\n"
+ " I Info\n"
+ " W Warn\n"
+ " E Error\n"
+ " F Fatal\n"
+ " S Silent (suppress all output)\n"
+ "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
+ "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
+ "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
+ "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
+ "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
+ "or defaults to \"threadtime\"\n\n");
+}
+
+static void show_format_help() {
+ fprintf(stderr,
+ "-v <format>, --format=<format> options:\n"
+ " Sets log print format verb and adverbs, where <format> is:\n"
+ " brief long process raw tag thread threadtime time\n"
+ " and individually flagged modifying adverbs can be added:\n"
+ " color descriptive epoch monotonic printable uid usec UTC year zone\n"
+ "\nSingle format verbs:\n"
+ " brief — Display priority/tag and PID of the process issuing the message.\n"
+ " long — Display all metadata fields, separate messages with blank lines.\n"
+ " process — Display PID only.\n"
+ " raw — Display the raw log message, with no other metadata fields.\n"
+ " tag — Display the priority/tag only.\n"
+ " thread — Display priority, PID and TID of process issuing the message.\n"
+ " threadtime — Display the date, invocation time, priority, tag, and the PID\n"
+ " and TID of the thread issuing the message. (the default format).\n"
+ " time — Display the date, invocation time, priority/tag, and PID of the\n"
+ " process issuing the message.\n"
+ "\nAdverb modifiers can be used in combination:\n"
+ " color — Display in highlighted color to match priority. i.e. \x1B[38;5;231mVERBOSE\n"
+ " \x1B[38;5;75mDEBUG \x1B[38;5;40mINFO \x1B[38;5;166mWARNING \x1B[38;5;196mERROR FATAL\x1B[0m\n"
+ " descriptive — events logs only, descriptions from event-log-tags database.\n"
+ " epoch — Display time as seconds since Jan 1 1970.\n"
+ " monotonic — Display time as cpu seconds since last boot.\n"
+ " printable — Ensure that any binary logging content is escaped.\n"
+ " uid — If permitted, display the UID or Android ID of logged process.\n"
+ " usec — Display time down the microsecond precision.\n"
+ " UTC — Display time as UTC.\n"
+ " year — Add the year to the displayed time.\n"
+ " zone — Add the local timezone to the displayed time.\n"
+ " \"<zone>\" — Print using this public named timezone (experimental).\n\n"
+ );
+}
+// clang-format on
+
+int Logcat::SetLogFormat(const char* format_string) {
+ AndroidLogPrintFormat format = android_log_formatFromString(format_string);
+
+ // invalid string?
+ if (format == FORMAT_OFF) return -1;
+
+ return android_log_setPrintFormat(logformat_.get(), format);
+}
+
+static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
+ static const char multipliers[][3] = {{""}, {"Ki"}, {"Mi"}, {"Gi"}};
+ size_t i;
+ for (i = 0;
+ (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
+ value /= 1024, ++i)
+ ;
+ return std::make_pair(value, multipliers[i]);
+}
+
+static char* parseTime(log_time& t, const char* cp) {
+ char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
+ if (ep) return ep;
+ ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+ if (ep) return ep;
+ return t.strptime(cp, "%s.%q");
+}
+
+// Find last logged line in <outputFileName>, or <outputFileName>.1
+static log_time lastLogTime(const char* outputFileName) {
+ log_time retval(log_time::EPOCH);
+ if (!outputFileName) return retval;
+
+ std::string directory;
+ const char* file = strrchr(outputFileName, '/');
+ if (!file) {
+ directory = ".";
+ file = outputFileName;
+ } else {
+ directory = std::string(outputFileName, file - outputFileName);
+ ++file;
+ }
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
+ closedir);
+ if (!dir.get()) return retval;
+
+ log_time now(android_log_clockid());
+
+ size_t len = strlen(file);
+ log_time modulo(0, NS_PER_SEC);
+ struct dirent* dp;
+
+ while (!!(dp = readdir(dir.get()))) {
+ if ((dp->d_type != DT_REG) || !!strncmp(dp->d_name, file, len) ||
+ (dp->d_name[len] && ((dp->d_name[len] != '.') ||
+ (strtoll(dp->d_name + 1, nullptr, 10) != 1)))) {
+ continue;
+ }
+
+ std::string file_name = directory;
+ file_name += "/";
+ file_name += dp->d_name;
+ std::string file;
+ if (!android::base::ReadFileToString(file_name, &file)) continue;
+
+ bool found = false;
+ for (const auto& line : android::base::Split(file, "\n")) {
+ log_time t(log_time::EPOCH);
+ char* ep = parseTime(t, line.c_str());
+ if (!ep || (*ep != ' ')) continue;
+ // determine the time precision of the logs (eg: msec or usec)
+ for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
+ if (t.tv_nsec % (mod * 10)) {
+ modulo.tv_nsec = mod;
+ break;
+ }
+ }
+ // We filter any times later than current as we may not have the
+ // year stored with each log entry. Also, since it is possible for
+ // entries to be recorded out of order (very rare) we select the
+ // maximum we find just in case.
+ if ((t < now) && (t > retval)) {
+ retval = t;
+ found = true;
+ }
+ }
+ // We count on the basename file to be the definitive end, so stop here.
+ if (!dp->d_name[len] && found) break;
+ }
+ if (retval == log_time::EPOCH) return retval;
+ // tail_time prints matching or higher, round up by the modulo to prevent
+ // a replay of the last entry we have just checked.
+ retval += modulo;
+ return retval;
+}
+
+void ReportErrorName(const std::string& name, bool allow_security,
+ std::vector<std::string>* errors) {
+ if (allow_security || name != "security") {
+ errors->emplace_back(name);
+ }
+}
+
+int Logcat::Run(int argc, char** argv) {
+ bool hasSetLogFormat = false;
+ bool clearLog = false;
+ bool security_buffer_selected =
+ false; // Do not report errors on the security buffer unless it is explicitly named.
+ bool getLogSize = false;
+ bool getPruneList = false;
+ bool printStatistics = false;
+ bool printDividers = false;
+ unsigned long setLogSize = 0;
+ const char* setPruneList = nullptr;
+ const char* setId = nullptr;
+ int mode = ANDROID_LOG_RDONLY;
+ std::string forceFilters;
+ size_t tail_lines = 0;
+ log_time tail_time(log_time::EPOCH);
+ size_t pid = 0;
+ bool got_t = false;
+ unsigned id_mask = 0;
+
+ if (argc == 2 && !strcmp(argv[1], "--help")) {
+ show_help();
+ return EXIT_SUCCESS;
+ }
+
+ // meant to catch comma-delimited values, but cast a wider
+ // net for stability dealing with possible mistaken inputs.
+ static const char delimiters[] = ",:; \t\n\r\f";
+
+ optind = 0;
+ while (true) {
+ int option_index = 0;
+ // list of long-argument only strings for later comparison
+ static const char pid_str[] = "pid";
+ static const char debug_str[] = "debug";
+ static const char id_str[] = "id";
+ static const char wrap_str[] = "wrap";
+ static const char print_str[] = "print";
+ // clang-format off
+ static const struct option long_options[] = {
+ { "binary", no_argument, nullptr, 'B' },
+ { "buffer", required_argument, nullptr, 'b' },
+ { "buffer-size", optional_argument, nullptr, 'g' },
+ { "clear", no_argument, nullptr, 'c' },
+ { debug_str, no_argument, nullptr, 0 },
+ { "dividers", no_argument, nullptr, 'D' },
+ { "file", required_argument, nullptr, 'f' },
+ { "format", required_argument, nullptr, 'v' },
+ // hidden and undocumented reserved alias for --regex
+ { "grep", required_argument, nullptr, 'e' },
+ // hidden and undocumented reserved alias for --max-count
+ { "head", required_argument, nullptr, 'm' },
+ { "help", no_argument, nullptr, 'h' },
+ { id_str, required_argument, nullptr, 0 },
+ { "last", no_argument, nullptr, 'L' },
+ { "max-count", required_argument, nullptr, 'm' },
+ { pid_str, required_argument, nullptr, 0 },
+ { print_str, no_argument, nullptr, 0 },
+ { "prune", optional_argument, nullptr, 'p' },
+ { "regex", required_argument, nullptr, 'e' },
+ { "rotate-count", required_argument, nullptr, 'n' },
+ { "rotate-kbytes", required_argument, nullptr, 'r' },
+ { "statistics", no_argument, nullptr, 'S' },
+ // hidden and undocumented reserved alias for -t
+ { "tail", required_argument, nullptr, 't' },
+ // support, but ignore and do not document, the optional argument
+ { wrap_str, optional_argument, nullptr, 0 },
+ { nullptr, 0, nullptr, 0 }
+ };
+ // clang-format on
+
+ int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+ &option_index);
+ if (c == -1) break;
+
+ switch (c) {
+ case 0:
+ // only long options
+ if (long_options[option_index].name == pid_str) {
+ if (pid != 0) {
+ error(EXIT_FAILURE, 0, "Only one --pid argument can be provided.");
+ }
+
+ if (!ParseUint(optarg, &pid) || pid < 1) {
+ error(EXIT_FAILURE, 0, "%s %s out of range.",
+ long_options[option_index].name, optarg);
+ }
+ break;
+ }
+ if (long_options[option_index].name == wrap_str) {
+ mode |= ANDROID_LOG_WRAP | ANDROID_LOG_RDONLY |
+ ANDROID_LOG_NONBLOCK;
+ // ToDo: implement API that supports setting a wrap timeout
+ size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
+ if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
+ error(EXIT_FAILURE, 0, "%s %s out of range.",
+ long_options[option_index].name, optarg);
+ }
+ if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
+ long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
+ dummy);
+ }
+ break;
+ }
+ if (long_options[option_index].name == print_str) {
+ print_it_anyways_ = true;
+ break;
+ }
+ if (long_options[option_index].name == debug_str) {
+ debug_ = true;
+ break;
+ }
+ if (long_options[option_index].name == id_str) {
+ setId = (optarg && optarg[0]) ? optarg : nullptr;
+ }
+ break;
+
+ case 's':
+ // default to all silent
+ android_log_addFilterRule(logformat_.get(), "*:s");
+ break;
+
+ case 'c':
+ clearLog = true;
+ mode |= ANDROID_LOG_WRONLY;
+ break;
+
+ case 'L':
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE |
+ ANDROID_LOG_NONBLOCK;
+ break;
+
+ case 'd':
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ break;
+
+ case 't':
+ got_t = true;
+ mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+ FALLTHROUGH_INTENDED;
+ case 'T':
+ if (strspn(optarg, "0123456789") != strlen(optarg)) {
+ char* cp = parseTime(tail_time, optarg);
+ if (!cp) {
+ error(EXIT_FAILURE, 0, "-%c '%s' not in time format.", c, optarg);
+ }
+ if (*cp) {
+ char ch = *cp;
+ *cp = '\0';
+ fprintf(stderr, "WARNING: -%c '%s' '%c%s' time truncated\n", c, optarg, ch,
+ cp + 1);
+ *cp = ch;
+ }
+ } else {
+ if (!ParseUint(optarg, &tail_lines) || tail_lines < 1) {
+ fprintf(stderr, "WARNING: -%c %s invalid, setting to 1\n", c, optarg);
+ tail_lines = 1;
+ }
+ }
+ break;
+
+ case 'D':
+ printDividers = true;
+ break;
+
+ case 'e':
+ regex_.reset(new std::regex(optarg));
+ break;
+
+ case 'm': {
+ if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {
+ error(EXIT_FAILURE, 0, "-%c '%s' isn't an integer greater than zero.", c,
+ optarg);
+ }
+ } break;
+
+ case 'g':
+ if (!optarg) {
+ getLogSize = true;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+
+ case 'G': {
+ if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {
+ error(EXIT_FAILURE, 0, "-G must be specified as <num><multiplier>.");
+ }
+ } break;
+
+ case 'p':
+ if (!optarg) {
+ getPruneList = true;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+
+ case 'P':
+ setPruneList = optarg;
+ break;
+
+ case 'b':
+ for (const auto& buffer : Split(optarg, delimiters)) {
+ if (buffer == "default") {
+ id_mask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH);
+ } else if (buffer == "all") {
+ id_mask = -1;
+ } else {
+ log_id_t log_id = android_name_to_log_id(buffer.c_str());
+ if (log_id >= LOG_ID_MAX) {
+ error(EXIT_FAILURE, 0, "Unknown buffer '%s' listed for -b.",
+ buffer.c_str());
+ }
+ if (log_id == LOG_ID_SECURITY) {
+ security_buffer_selected = true;
+ }
+ id_mask |= (1 << log_id);
+ }
+ }
+ break;
+
+ case 'B':
+ print_binary_ = 1;
+ break;
+
+ case 'f':
+ if ((tail_time == log_time::EPOCH) && !tail_lines) {
+ tail_time = lastLogTime(optarg);
+ }
+ // redirect output to a file
+ output_file_name_ = optarg;
+ break;
+
+ case 'r':
+ if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {
+ error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -r.", optarg);
+ }
+ break;
+
+ case 'n':
+ if (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {
+ error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -n.", optarg);
+ }
+ break;
+
+ case 'v':
+ if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
+ show_format_help();
+ return EXIT_SUCCESS;
+ }
+ for (const auto& arg : Split(optarg, delimiters)) {
+ int err = SetLogFormat(arg.c_str());
+ if (err < 0) {
+ error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -v.", arg.c_str());
+ }
+ if (err) hasSetLogFormat = true;
+ }
+ break;
+
+ case 'Q':
+#define LOGCAT_FILTER "androidboot.logcat="
+#define CONSOLE_PIPE_OPTION "androidboot.consolepipe="
+#define CONSOLE_OPTION "androidboot.console="
+#define QEMU_PROPERTY "ro.kernel.qemu"
+#define QEMU_CMDLINE "qemu.cmdline"
+ // This is a *hidden* option used to start a version of logcat
+ // in an emulated device only. It basically looks for
+ // androidboot.logcat= on the kernel command line. If
+ // something is found, it extracts a log filter and uses it to
+ // run the program. The logcat output will go to consolepipe if
+ // androiboot.consolepipe (e.g. qemu_pipe) is given, otherwise,
+ // it goes to androidboot.console (e.g. tty)
+ {
+ // if not in emulator, exit quietly
+ if (false == android::base::GetBoolProperty(QEMU_PROPERTY, false)) {
+ return EXIT_SUCCESS;
+ }
+
+ std::string cmdline = android::base::GetProperty(QEMU_CMDLINE, "");
+ if (cmdline.empty()) {
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ }
+
+ const char* logcatFilter = strstr(cmdline.c_str(), LOGCAT_FILTER);
+ // if nothing found or invalid filters, exit quietly
+ if (!logcatFilter) {
+ return EXIT_SUCCESS;
+ }
+
+ const char* p = logcatFilter + strlen(LOGCAT_FILTER);
+ const char* q = strpbrk(p, " \t\n\r");
+ if (!q) q = p + strlen(p);
+ forceFilters = std::string(p, q);
+
+ // redirect our output to the emulator console pipe or console
+ const char* consolePipe =
+ strstr(cmdline.c_str(), CONSOLE_PIPE_OPTION);
+ const char* console =
+ strstr(cmdline.c_str(), CONSOLE_OPTION);
+
+ if (consolePipe) {
+ p = consolePipe + strlen(CONSOLE_PIPE_OPTION);
+ } else if (console) {
+ p = console + strlen(CONSOLE_OPTION);
+ } else {
+ return EXIT_FAILURE;
+ }
+
+ q = strpbrk(p, " \t\n\r");
+ int len = q ? q - p : strlen(p);
+ std::string devname = "/dev/" + std::string(p, len);
+ std::string pipePurpose("pipe:logcat");
+ if (consolePipe) {
+ // example: "qemu_pipe,pipe:logcat"
+ // upon opening of /dev/qemu_pipe, the "pipe:logcat"
+ // string with trailing '\0' should be written to the fd
+ size_t pos = devname.find(',');
+ if (pos != std::string::npos) {
+ pipePurpose = devname.substr(pos + 1);
+ devname = devname.substr(0, pos);
+ }
+ }
+
+ fprintf(stderr, "logcat using %s\n", devname.c_str());
+
+ int fd = open(devname.c_str(), O_WRONLY | O_CLOEXEC);
+ if (fd < 0) {
+ break;
+ }
+
+ if (consolePipe) {
+ // need the trailing '\0'
+ if (!android::base::WriteFully(fd, pipePurpose.c_str(),
+ pipePurpose.size() + 1)) {
+ close(fd);
+ return EXIT_FAILURE;
+ }
+ }
+ // close output and error channels, replace with console
+ dup2(fd, output_fd_.get());
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ break;
+
+ case 'S':
+ printStatistics = true;
+ break;
+
+ case ':':
+ error(EXIT_FAILURE, 0, "Option '%s' needs an argument.", argv[optind - 1]);
+ break;
+
+ case 'h':
+ show_help();
+ show_format_help();
+ return EXIT_SUCCESS;
+
+ case '?':
+ error(EXIT_FAILURE, 0, "Unknown option '%s'.", argv[optind - 1]);
+ break;
+
+ default:
+ error(EXIT_FAILURE, 0, "Unknown getopt_long() result '%c'.", c);
+ }
+ }
+
+ if (max_count_ && got_t) {
+ error(EXIT_FAILURE, 0, "Cannot use -m (--max-count) and -t together.");
+ }
+ if (print_it_anyways_ && (!regex_ || !max_count_)) {
+ // One day it would be nice if --print -v color and --regex <expr>
+ // could play with each other and show regex highlighted content.
+ fprintf(stderr,
+ "WARNING: "
+ "--print ignored, to be used in combination with\n"
+ " "
+ "--regex <expr> and --max-count <N>\n");
+ print_it_anyways_ = false;
+ }
+
+ // If no buffers are specified, default to using these buffers.
+ if (id_mask == 0) {
+ id_mask = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH) |
+ (1 << LOG_ID_KERNEL);
+ }
+
+ if (log_rotate_size_kb_ != 0 && !output_file_name_) {
+ error(EXIT_FAILURE, 0, "-r requires -f as well.");
+ }
+
+ if (setId != 0) {
+ if (!output_file_name_) {
+ error(EXIT_FAILURE, 0, "--id='%s' requires -f as well.", setId);
+ }
+
+ std::string file_name = StringPrintf("%s.id", output_file_name_);
+ std::string file;
+ bool file_ok = android::base::ReadFileToString(file_name, &file);
+ android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
+ getuid(), getgid());
+ if (!file_ok || !file.compare(setId)) setId = nullptr;
+ }
+
+ if (!hasSetLogFormat) {
+ const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+
+ if (!!logFormat) {
+ for (const auto& arg : Split(logFormat, delimiters)) {
+ int err = SetLogFormat(arg.c_str());
+ // environment should not cause crash of logcat
+ if (err < 0) {
+ fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg.c_str());
+ }
+ if (err > 0) hasSetLogFormat = true;
+ }
+ }
+ if (!hasSetLogFormat) {
+ SetLogFormat("threadtime");
+ }
+ }
+
+ if (forceFilters.size()) {
+ int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
+ if (err < 0) {
+ error(EXIT_FAILURE, 0, "Invalid filter expression in logcat args.");
+ }
+ } else if (argc == optind) {
+ // Add from environment variable
+ const char* env_tags_orig = getenv("ANDROID_LOG_TAGS");
+
+ if (!!env_tags_orig) {
+ int err = android_log_addFilterString(logformat_.get(), env_tags_orig);
+
+ if (err < 0) {
+ error(EXIT_FAILURE, 0, "Invalid filter expression in ANDROID_LOG_TAGS.");
+ }
+ }
+ } else {
+ // Add from commandline
+ for (int i = optind ; i < argc ; i++) {
+ int err = android_log_addFilterString(logformat_.get(), argv[i]);
+ if (err < 0) {
+ error(EXIT_FAILURE, 0, "Invalid filter expression '%s'.", argv[i]);
+ }
+ }
+ }
+
+ if (mode & ANDROID_LOG_PSTORE) {
+ if (output_file_name_) {
+ error(EXIT_FAILURE, 0, "-c is ambiguous with both -f and -L specified.");
+ }
+ if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+ error(EXIT_FAILURE, 0, "-L is incompatible with -g/-G, -S, and -p/-P.");
+ }
+ if (clearLog) {
+ unlink("/sys/fs/pstore/pmsg-ramoops-0");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ if (output_file_name_) {
+ if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+ error(EXIT_FAILURE, 0, "-f is incompatible with -g/-G, -S, and -p/-P.");
+ }
+
+ if (clearLog || setId) {
+ int max_rotation_count_digits =
+ max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
+
+ for (int i = max_rotated_logs_; i >= 0; --i) {
+ std::string file;
+
+ if (!i) {
+ file = output_file_name_;
+ } else {
+ file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
+ }
+
+ int err = unlink(file.c_str());
+
+ if (err < 0 && errno != ENOENT) {
+ fprintf(stderr, "failed to delete log file '%s': %s\n", file.c_str(),
+ strerror(errno));
+ }
+ }
+ }
+
+ if (clearLog) {
+ return EXIT_SUCCESS;
+ }
+ }
+
+ std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
+ nullptr, &android_logger_list_free};
+ if (tail_time != log_time::EPOCH) {
+ logger_list.reset(android_logger_list_alloc_time(mode, tail_time, pid));
+ } else {
+ logger_list.reset(android_logger_list_alloc(mode, tail_lines, pid));
+ }
+ // We have three orthogonal actions below to clear, set log size and
+ // get log size. All sharing the same iteration loop.
+ std::vector<std::string> open_device_failures;
+ std::vector<std::string> clear_failures;
+ std::vector<std::string> set_size_failures;
+ std::vector<std::string> get_size_failures;
+
+ for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+ if (!(id_mask & (1 << i))) continue;
+ const char* buffer_name = android_log_id_to_name(static_cast<log_id_t>(i));
+
+ auto logger = android_logger_open(logger_list.get(), static_cast<log_id_t>(i));
+ if (logger == nullptr) {
+ ReportErrorName(buffer_name, security_buffer_selected, &open_device_failures);
+ continue;
+ }
+
+ if (clearLog) {
+ if (android_logger_clear(logger)) {
+ ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
+ }
+ }
+
+ if (setLogSize) {
+ if (android_logger_set_log_size(logger, setLogSize)) {
+ ReportErrorName(buffer_name, security_buffer_selected, &set_size_failures);
+ }
+ }
+
+ if (getLogSize) {
+ long size = android_logger_get_log_size(logger);
+ long readable = android_logger_get_log_readable_size(logger);
+
+ if (size < 0 || readable < 0) {
+ ReportErrorName(buffer_name, security_buffer_selected, &get_size_failures);
+ } else {
+ auto size_format = format_of_size(size);
+ auto readable_format = format_of_size(readable);
+ std::string str = android::base::StringPrintf(
+ "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+ " max entry is %d B, max payload is %d B\n",
+ buffer_name, size_format.first, size_format.second, readable_format.first,
+ readable_format.second, (int)LOGGER_ENTRY_MAX_LEN,
+ (int)LOGGER_ENTRY_MAX_PAYLOAD);
+ TEMP_FAILURE_RETRY(write(output_fd_.get(), str.data(), str.length()));
+ }
+ }
+ }
+
+ // report any errors in the above loop and exit
+ if (!open_device_failures.empty()) {
+ error(EXIT_FAILURE, 0, "Unable to open log device%s '%s'.",
+ open_device_failures.size() > 1 ? "s" : "", Join(open_device_failures, ",").c_str());
+ }
+ if (!clear_failures.empty()) {
+ error(EXIT_FAILURE, 0, "failed to clear the '%s' log%s.", Join(clear_failures, ",").c_str(),
+ clear_failures.size() > 1 ? "s" : "");
+ }
+ if (!set_size_failures.empty()) {
+ error(EXIT_FAILURE, 0, "failed to set the '%s' log size%s.",
+ Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
+ }
+ if (!get_size_failures.empty()) {
+ error(EXIT_FAILURE, 0, "failed to get the readable '%s' log size%s.",
+ Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
+ }
+
+ if (setPruneList) {
+ size_t len = strlen(setPruneList);
+ if (android_logger_set_prune_list(logger_list.get(), setPruneList, len)) {
+ error(EXIT_FAILURE, 0, "Failed to set the prune list.");
+ }
+ return EXIT_SUCCESS;
+ }
+
+ if (printStatistics || getPruneList) {
+ std::string buf(8192, '\0');
+ size_t ret_length = 0;
+ int retry = 32;
+
+ for (; retry >= 0; --retry) {
+ if (getPruneList) {
+ android_logger_get_prune_list(logger_list.get(), buf.data(), buf.size());
+ } else {
+ android_logger_get_statistics(logger_list.get(), buf.data(), buf.size());
+ }
+
+ ret_length = atol(buf.c_str());
+ if (ret_length < 3) {
+ error(EXIT_FAILURE, 0, "Failed to read data.");
+ }
+
+ if (ret_length < buf.size()) {
+ break;
+ }
+
+ buf.resize(ret_length + 1);
+ }
+
+ if (retry < 0) {
+ error(EXIT_FAILURE, 0, "Failed to read data.");
+ }
+
+ buf.resize(ret_length);
+ if (buf.back() == '\f') {
+ buf.pop_back();
+ }
+
+ // Remove the byte count prefix
+ const char* cp = buf.c_str();
+ while (isdigit(*cp)) ++cp;
+ if (*cp == '\n') ++cp;
+
+ size_t len = strlen(cp);
+ TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
+ return EXIT_SUCCESS;
+ }
+
+ if (getLogSize || setLogSize || clearLog) return EXIT_SUCCESS;
+
+ SetupOutputAndSchedulingPolicy(!(mode & ANDROID_LOG_NONBLOCK));
+
+ while (!max_count_ || print_count_ < max_count_) {
+ struct log_msg log_msg;
+ int ret = android_logger_list_read(logger_list.get(), &log_msg);
+ if (!ret) {
+ error(EXIT_FAILURE, 0, R"init(Unexpected EOF!
+
+This means that either the device shut down, logd crashed, or this instance of logcat was unable to read log
+messages as quickly as they were being produced.
+
+If you have enabled significant logging, look into using the -G option to increase log buffer sizes.)init");
+ }
+
+ if (ret < 0) {
+ if (ret == -EAGAIN) break;
+
+ if (ret == -EIO) {
+ error(EXIT_FAILURE, 0, "Unexpected EOF!");
+ }
+ if (ret == -EINVAL) {
+ error(EXIT_FAILURE, 0, "Unexpected length.");
+ }
+ error(EXIT_FAILURE, errno, "Logcat read failure");
+ }
+
+ if (log_msg.id() > LOG_ID_MAX) {
+ error(EXIT_FAILURE, 0, "Unexpected log id (%d) over LOG_ID_MAX (%d).", log_msg.id(),
+ LOG_ID_MAX);
+ }
+
+ PrintDividers(log_msg.id(), printDividers);
+
+ if (print_binary_) {
+ TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
+ } else {
+ ProcessBuffer(&log_msg);
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char** argv) {
+ Logcat logcat;
+ return logcat.Run(argc, argv);
+}
diff --git a/logcat/logcatd b/logcat/logcatd
new file mode 100755
index 000000000..5a1415d11
--- /dev/null
+++ b/logcat/logcatd
@@ -0,0 +1,29 @@
+#! /system/bin/sh
+
+# This is primarily meant to be used by logpersist. This script is run as an init service, which
+# first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
+# `-L` to read the current logcat buffers to persistent storage.
+
+# init sets the umask to 077 for forked processes. logpersist needs to create files that are group
+# readable. So relax the umask to only disallow group wx and world rwx.
+umask 037
+
+has_last="false"
+for arg in "$@"; do
+ if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
+ has_last="true"
+ fi
+done
+
+if [ "$has_last" == "true" ]; then
+ logcat "$@"
+fi
+
+args_without_last=()
+for arg in "$@"; do
+ if [ "$arg" != "-L" -a "$arg" != "--last" ]; then
+ ARGS+=("$arg")
+ fi
+done
+
+exec logcat "${ARGS[@]}"
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
new file mode 100644
index 000000000..64d5500fa
--- /dev/null
+++ b/logcat/logcatd.rc
@@ -0,0 +1,62 @@
+#
+# init scriptures for logcatd persistent logging.
+#
+# Make sure any property changes are only performed with /data mounted, after
+# post-fs-data state because otherwise behavior is undefined. The exceptions
+# are device adjustments for logcatd service properties (persist.* overrides
+# notwithstanding) for logd.logpersistd.size logd.logpersistd.rotate_kbytes and
+# logd.logpersistd.buffer.
+
+# persist to non-persistent trampolines to permit device properties can be
+# overridden when /data mounts, or during runtime.
+on property:persist.logd.logpersistd.count=*
+ # expect /init to report failure if property empty (default)
+ setprop persist.logd.logpersistd.size ${persist.logd.logpersistd.count}
+
+on property:persist.logd.logpersistd.size=*
+ setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
+
+on property:persist.logd.logpersistd.rotate_kbytes=*
+ setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
+
+on property:persist.logd.logpersistd.buffer=*
+ setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
+
+on property:persist.logd.logpersistd=logcatd
+ setprop logd.logpersistd logcatd
+
+# enable, prep and start logcatd service
+on load_persist_props_action
+ setprop logd.logpersistd.enable true
+
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
+ # log group should be able to read persisted logs
+ mkdir /data/misc/logd 0750 logd log
+ start logcatd
+
+# stop logcatd service and clear data
+on property:logd.logpersistd.enable=true && property:logd.logpersistd=clear
+ setprop persist.logd.logpersistd ""
+ stop logcatd
+ # logd for clear of only our files in /data/misc/logd
+ exec - logd log -- /system/bin/logcat -c -f /data/misc/logd/logcat -n ${logd.logpersistd.size:-256}
+ setprop logd.logpersistd ""
+
+# stop logcatd service
+on property:logd.logpersistd=stop
+ setprop persist.logd.logpersistd ""
+ stop logcatd
+ setprop logd.logpersistd ""
+
+on property:logd.logpersistd.enable=false
+ stop logcatd
+
+# logcatd service
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-2048} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+ class late_start
+ disabled
+ # logd for write to /data/misc/logd, log group for read from log daemon
+ user logd
+ group log
+ writepid /dev/cpuset/system-background/tasks
+ oom_score_adjust -600
diff --git a/logcat/logpersist b/logcat/logpersist
new file mode 100755
index 000000000..05b46f047
--- /dev/null
+++ b/logcat/logpersist
@@ -0,0 +1,178 @@
+#! /system/bin/sh
+# logpersist cat, start and stop handlers
+progname="${0##*/}"
+case `getprop ro.debuggable` in
+1) ;;
+*) echo "${progname} - Permission denied"
+ exit 1
+ ;;
+esac
+
+property=persist.logd.logpersistd
+
+case `getprop ${property#persist.}.enable` in
+true) ;;
+*) echo "${progname} - Disabled"
+ exit 1
+ ;;
+esac
+
+log_uid=logd
+log_tag_property=persist.log.tag
+data=/data/misc/logd/logcat
+service=logcatd
+size_default=256
+buffer_default=all
+args="${@}"
+
+size=${size_default}
+buffer=${buffer_default}
+clear=false
+while [ ${#} -gt 0 ]; do
+ case ${1} in
+ -c|--clear) clear=true ;;
+ --size=*) size="${1#--size=}" ;;
+ --rotate-count=*) size="${1#--rotate-count=}" ;;
+ -n|--size|--rotate-count) size="${2}" ; shift ;;
+ --buffer=*) buffer="${1#--buffer=}" ;;
+ -b|--buffer) buffer="${2}" ; shift ;;
+ -h|--help|*)
+ LEAD_SPACE_="`echo ${progname%.*} | tr '[ -~]' ' '`"
+ echo "${progname%.*}.cat - dump current ${service} logs"
+ echo "${progname%.*}.start [--size=<size_in_kb>] [--buffer=<buffers>] [--clear]"
+ echo "${LEAD_SPACE_} - start ${service} service"
+ echo "${progname%.*}.stop [--clear] - stop ${service} service"
+ case ${1} in
+ -h|--help) exit 0 ;;
+ *) echo ERROR: bad argument ${@} >&2 ; exit 1 ;;
+ esac
+ ;;
+ esac
+ shift
+done
+
+if [ -z "${size}" -o "${size_default}" = "${size}" ]; then
+ unset size
+fi
+if [ -n "${size}" ] &&
+ ! ( [ 0 -lt "${size}" ] && [ 2048 -ge "${size}" ] ) >/dev/null 2>&1; then
+ echo ERROR: Invalid --size ${size} >&2
+ exit 1
+fi
+if [ -z "${buffer}" -o "${buffer_default}" = "${buffer}" ]; then
+ unset buffer
+fi
+if [ -n "${buffer}" ] && ! logcat -b ${buffer} -g >/dev/null 2>&1; then
+ echo ERROR: Invalid --buffer ${buffer} >&2
+ exit 1
+fi
+
+log_tag="`getprop ${log_tag_property}`"
+logd_logpersistd="`getprop ${property}`"
+
+case ${progname} in
+*.cat)
+ if [ -n "${size}${buffer}" -o "true" = "${clear}" ]; then
+ echo WARNING: Can not use --clear, --size or --buffer with ${progname%.*}.cat >&2
+ fi
+ su ${log_uid} ls "${data%/*}" |
+ tr -d '\r' |
+ sort -ru |
+ sed "s#^#${data%/*}/#" |
+ grep "${data}[.]*[0-9]*\$" |
+ su ${log_uid} xargs cat
+ ;;
+*.start)
+ current_buffer="`getprop ${property#persist.}.buffer`"
+ current_size="`getprop ${property#persist.}.size`"
+ if [ "${service}" = "`getprop ${property#persist.}`" ]; then
+ if [ "true" = "${clear}" ]; then
+ setprop ${property#persist.} "clear"
+ elif [ "${buffer}|${size}" != "${current_buffer}|${current_size}" ]; then
+ echo "ERROR: Changing existing collection parameters from" >&2
+ if [ "${buffer}" != "${current_buffer}" ]; then
+ a=${current_buffer}
+ b=${buffer}
+ if [ -z "${a}" ]; then a="${default_buffer}"; fi
+ if [ -z "${b}" ]; then b="${default_buffer}"; fi
+ echo " --buffer ${a} to ${b}" >&2
+ fi
+ if [ "${size}" != "${current_size}" ]; then
+ a=${current_size}
+ b=${size}
+ if [ -z "${a}" ]; then a="${default_size}"; fi
+ if [ -z "${b}" ]; then b="${default_size}"; fi
+ echo " --size ${a} to ${b}" >&2
+ fi
+ echo " Are you sure you want to do this?" >&2
+ echo " Suggest add --clear to erase data and restart with new settings." >&2
+ echo " To blindly override and retain data, ${progname%.*}.stop first." >&2
+ exit 1
+ fi
+ elif [ "true" = "${clear}" ]; then
+ setprop ${property#persist.} "clear"
+ fi
+ if [ -n "${buffer}${current_buffer}" ]; then
+ setprop ${property}.buffer "${buffer}"
+ if [ -z "${buffer}" ]; then
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.buffer ""
+ fi
+ fi
+ if [ -n "${size}${current_size}" ]; then
+ setprop ${property}.size "${size}"
+ if [ -z "${size}" ]; then
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.size ""
+ fi
+ fi
+ while [ "clear" = "`getprop ${property#persist.}`" ]; do
+ continue
+ done
+ # Tell Settings that we are back on again if we turned logging off
+ tag="${log_tag#Settings}"
+ if [ X"${log_tag}" != X"${tag}" ]; then
+ echo "WARNING: enabling logd service" >&2
+ setprop ${log_tag_property} "${tag#,}"
+ fi
+ # ${service}.rc does the heavy lifting with the following trigger
+ setprop ${property} ${service}
+ # 20ms done, to permit process feedback check
+ sleep 1
+ getprop ${property#persist.}
+ # also generate an error return code if not found running
+ pgrep -u ${log_uid} ${service%d}
+ ;;
+*.stop)
+ if [ -n "${size}${buffer}" ]; then
+ echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
+ fi
+ if [ "true" = "${clear}" ]; then
+ setprop ${property#persist.} "clear"
+ else
+ setprop ${property#persist.} "stop"
+ fi
+ if [ -n "`getprop ${property#persist.}.buffer`" ]; then
+ setprop ${property}.buffer ""
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.buffer ""
+ fi
+ if [ -n "`getprop ${property#persist.}.size`" ]; then
+ setprop ${property}.size ""
+ # deal with trampoline for empty properties
+ setprop ${property#persist.}.size ""
+ fi
+ while [ "clear" = "`getprop ${property#persist.}`" ]; do
+ continue
+ done
+ ;;
+*)
+ echo "ERROR: Unexpected command ${0##*/} ${args}" >&2
+ exit 1
+esac
+
+if [ X"${log_tag}" != X"`getprop ${log_tag_property}`" ] ||
+ [ X"${logd_logpersistd}" != X"`getprop ${property}`" ]; then
+ echo "WARNING: killing Settings application to pull in new values" >&2
+ am force-stop com.android.settings
+fi
diff --git a/logcat/tests/Android.bp b/logcat/tests/Android.bp
new file mode 100644
index 000000000..ab84150d5
--- /dev/null
+++ b/logcat/tests/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2013-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "logcat-tests-defaults",
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+}
+
+// -----------------------------------------------------------------------------
+// Benchmarks
+// ----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+// adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
+cc_benchmark {
+ name: "logcat-benchmarks",
+ defaults: ["logcat-tests-defaults"],
+ srcs: ["logcat_benchmark.cpp"],
+ shared_libs: ["libbase"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+// Build tests for the device (with .so). Run with:
+// adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
+cc_test {
+ name: "logcat-unit-tests",
+ defaults: ["logcat-tests-defaults"],
+ shared_libs: ["libbase"],
+ static_libs: ["liblog"],
+ srcs: [
+ "logcat_test.cpp",
+ "logcatd_test.cpp",
+ ],
+}
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
new file mode 100644
index 000000000..8d8862889
--- /dev/null
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <benchmark/benchmark.h>
+
+static const char begin[] = "--------- beginning of ";
+
+static void BM_logcat_sorted_order(benchmark::State& state) {
+ FILE* fp;
+
+ if (!state.KeepRunning()) return;
+
+ fp = popen(
+ "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
+ "r");
+ if (!fp) return;
+
+ class timestamp {
+ private:
+ int month;
+ int day;
+ int hour;
+ int minute;
+ int second;
+ int millisecond;
+ bool ok;
+
+ public:
+ void init(const char* buffer) {
+ ok = false;
+ if (buffer != NULL) {
+ ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", &month, &day, &hour,
+ &minute, &second, &millisecond) == 6;
+ }
+ }
+
+ explicit timestamp(const char* buffer) {
+ init(buffer);
+ }
+
+ bool operator<(timestamp& T) {
+ return !ok || !T.ok || (month < T.month) ||
+ ((month == T.month) &&
+ ((day < T.day) ||
+ ((day == T.day) &&
+ ((hour < T.hour) ||
+ ((hour == T.hour) &&
+ ((minute < T.minute) ||
+ ((minute == T.minute) &&
+ ((second < T.second) ||
+ ((second == T.second) &&
+ (millisecond < T.millisecond))))))))));
+ }
+
+ bool valid(void) {
+ return ok;
+ }
+ } last(NULL);
+
+ char* last_buffer = NULL;
+ char buffer[5120];
+
+ int count = 0;
+ int next_lt_last = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+ if (!last.valid()) {
+ free(last_buffer);
+ last_buffer = strdup(buffer);
+ last.init(buffer);
+ }
+ timestamp next(buffer);
+ if (next < last) {
+ if (last_buffer) {
+ fprintf(stderr, "<%s", last_buffer);
+ }
+ fprintf(stderr, ">%s", buffer);
+ ++next_lt_last;
+ }
+ if (next.valid()) {
+ free(last_buffer);
+ last_buffer = strdup(buffer);
+ last.init(buffer);
+ }
+ ++count;
+ }
+ free(last_buffer);
+
+ pclose(fp);
+
+ static const int max_ok = 2;
+
+ // Allow few fails, happens with readers active
+ fprintf(stderr, "%s: %d/%d out of order entries\n",
+ (next_lt_last) ? ((next_lt_last <= max_ok) ? "WARNING" : "ERROR")
+ : "INFO",
+ next_lt_last, count);
+
+ if (next_lt_last > max_ok) {
+ fprintf(stderr, "EXPECT_GE(max_ok=%d, next_lt_last=%d)\n", max_ok,
+ next_lt_last);
+ }
+
+ // sample statistically too small
+ if (count < 100) {
+ fprintf(stderr, "EXPECT_LT(100, count=%d)\n", count);
+ }
+
+ state.KeepRunning();
+}
+BENCHMARK(BM_logcat_sorted_order);
+
+BENCHMARK_MAIN();
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
new file mode 100644
index 000000000..b32b43737
--- /dev/null
+++ b/logcat/tests/logcat_test.cpp
@@ -0,0 +1,1760 @@
+/*
+ * Copyright (C) 2013-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <log/event_tag_map.h>
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#ifndef logcat_executable
+#define USING_LOGCAT_EXECUTABLE_DEFAULT
+#define logcat_executable "logcat"
+#endif
+
+#define BIG_BUFFER (5 * 1024)
+
+// rest(), let the logs settle.
+//
+// logd is in a background cgroup and under extreme load can take up to
+// 3 seconds to land a log entry. Under moderate load we can do with 200ms.
+static void rest() {
+ static const useconds_t restPeriod = 200000;
+
+ usleep(restPeriod);
+}
+
+// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
+// non-syscall libs. Since we are only using this in the emergency of
+// a signal to stuff a terminating code into the logs, we will spin rather
+// than try a usleep.
+#define LOG_FAILURE_RETRY(exp) \
+ ({ \
+ typeof(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (((_rc == -1) && ((errno == EINTR) || (errno == EAGAIN))) || \
+ (_rc == -EINTR) || (_rc == -EAGAIN)); \
+ _rc; \
+ })
+
+static const char begin[] = "--------- beginning of ";
+
+TEST(logcat, buckets) {
+ FILE* fp;
+
+#undef LOG_TAG
+#define LOG_TAG "inject.buckets"
+ // inject messages into radio, system, main and events buffers to
+ // ensure that we see all the begin[] bucket messages.
+ RLOGE(logcat_executable);
+ SLOGE(logcat_executable);
+ ALOGE(logcat_executable);
+ __android_log_bswrite(0, logcat_executable ".inject.buckets");
+ rest();
+
+ ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+ " -b radio -b events -b system -b main -d 2>/dev/null",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int ids = 0;
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ while (char* cp = strrchr(buffer, '\n')) {
+ *cp = '\0';
+ }
+ log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1);
+ ids |= 1 << id;
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ EXPECT_EQ(ids, 15);
+
+ EXPECT_EQ(count, 4);
+}
+
+TEST(logcat, event_tag_filter) {
+ FILE* fp;
+
+#undef LOG_TAG
+#define LOG_TAG "inject.filter"
+ // inject messages into radio, system and main buffers
+ // with our unique log tag to test logcat filter.
+ RLOGE(logcat_executable);
+ SLOGE(logcat_executable);
+ ALOGE(logcat_executable);
+ rest();
+
+ std::string command = android::base::StringPrintf(
+ logcat_executable
+ " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
+ getpid());
+ ASSERT_TRUE(NULL != (fp = popen(command.c_str(), "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
+ }
+
+ pclose(fp);
+
+ // logcat, liblogcat and logcatd test instances result in the progression
+ // of 3, 6 and 9 for our counts as each round is performed.
+ EXPECT_GE(count, 3);
+ EXPECT_LE(count, 9);
+ EXPECT_EQ(count % 3, 0);
+}
+
+// If there is not enough background noise in the logs, then spam the logs to
+// permit tail checking so that the tests can progress.
+static size_t inject(ssize_t count) {
+ if (count <= 0) return 0;
+
+ static const size_t retry = 4;
+ size_t errors = retry;
+ size_t num = 0;
+ for (;;) {
+ log_time ts(CLOCK_MONOTONIC);
+ if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
+ if (++num >= (size_t)count) {
+ // let data settle end-to-end
+ sleep(3);
+ return num;
+ }
+ errors = retry;
+ usleep(100); // ~32 per timer tick, we are a spammer regardless
+ } else if (--errors <= 0) {
+ return num;
+ }
+ }
+ // NOTREACH
+ return num;
+}
+
+TEST(logcat, year) {
+ if (android_log_clockid() == CLOCK_MONOTONIC) {
+ fprintf(stderr, "Skipping test, logd is monotonic time\n");
+ return;
+ }
+
+ int count;
+ int tries = 3; // in case run too soon after system start or buffer clear
+
+ do {
+ FILE* fp;
+
+ char needle[32];
+ time_t now;
+ time(&now);
+ struct tm* ptm;
+#if !defined(_WIN32)
+ struct tm tmBuf;
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&&now);
+#endif
+ strftime(needle, sizeof(needle), "[ %Y-", ptm);
+
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable " -v long -v year -b all -t 3 2>/dev/null", "r")));
+
+ char buffer[BIG_BUFFER];
+
+ count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(buffer, needle, strlen(needle))) {
+ ++count;
+ }
+ }
+ pclose(fp);
+
+ } while ((count < 3) && --tries && inject(3 - count));
+
+ ASSERT_EQ(3, count);
+}
+
+// Return a pointer to each null terminated -v long time field.
+static char* fgetLongTime(char* buffer, size_t buflen, FILE* fp) {
+ while (fgets(buffer, buflen, fp)) {
+ char* cp = buffer;
+ if (*cp != '[') {
+ continue;
+ }
+ while (*++cp == ' ') {
+ ;
+ }
+ char* ep = cp;
+ while (isdigit(*ep)) {
+ ++ep;
+ }
+ if ((*ep != '-') && (*ep != '.')) {
+ continue;
+ }
+ // Find PID field. Look for ': ' or ':[0-9][0-9][0-9]'
+ while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
+ if (isdigit(ep[0]) && isdigit(ep[1]) && isdigit(ep[2])) break;
+ }
+ if (!ep) {
+ continue;
+ }
+ static const size_t pid_field_width = 7;
+ ep -= pid_field_width;
+ *ep = '\0';
+ return cp;
+ }
+ return NULL;
+}
+
+TEST(logcat, tz) {
+ if (android_log_clockid() == CLOCK_MONOTONIC) {
+ fprintf(stderr, "Skipping test, logd is monotonic time\n");
+ return;
+ }
+
+ int tries = 4; // in case run too soon after system start or buffer clear
+ int count;
+
+ do {
+ FILE* fp;
+
+ ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+ " -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ count = 0;
+
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
+ ++count;
+ } else {
+ fprintf(stderr, "ts=\"%s\"\n", buffer + 2);
+ }
+ }
+
+ pclose(fp);
+
+ } while ((count < 3) && --tries && inject(3 - count));
+
+ ASSERT_EQ(3, count);
+}
+
+TEST(logcat, ntz) {
+ FILE* fp;
+
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable
+ " -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(0, count);
+}
+
+static void do_tail(int num) {
+ int tries = 4; // in case run too soon after system start or buffer clear
+ int count;
+
+ if (num > 10) ++tries;
+ if (num > 100) ++tries;
+ do {
+ char buffer[BIG_BUFFER];
+
+ snprintf(buffer, sizeof(buffer),
+ "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
+
+ FILE* fp;
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ count = 0;
+
+ while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+ ++count;
+ }
+
+ pclose(fp);
+
+ } while ((count < num) && --tries && inject(num - count));
+
+ ASSERT_EQ(num, count);
+}
+
+TEST(logcat, tail_3) {
+ do_tail(3);
+}
+
+TEST(logcat, tail_10) {
+ do_tail(10);
+}
+
+TEST(logcat, tail_100) {
+ do_tail(100);
+}
+
+TEST(logcat, tail_1000) {
+ do_tail(1000);
+}
+
+static void do_tail_time(const char* cmd) {
+ FILE* fp;
+ int count;
+ char buffer[BIG_BUFFER];
+ char* last_timestamp = NULL;
+ // Hard to predict 100% if first (overlap) or second line will match.
+ // -v nsec will in a substantial majority be the second line.
+ char* first_timestamp = NULL;
+ char* second_timestamp = NULL;
+ char* input;
+
+ int tries = 4; // in case run too soon after system start or buffer clear
+
+ do {
+ snprintf(buffer, sizeof(buffer), "%s -t 10 2>&1", cmd);
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+ count = 0;
+
+ while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ ++count;
+ if (!first_timestamp) {
+ first_timestamp = strdup(input);
+ } else if (!second_timestamp) {
+ second_timestamp = strdup(input);
+ }
+ free(last_timestamp);
+ last_timestamp = strdup(input);
+ }
+ pclose(fp);
+
+ } while ((count < 10) && --tries && inject(10 - count));
+
+ EXPECT_EQ(count, 10); // We want _some_ history, too small, falses below
+ EXPECT_TRUE(last_timestamp != NULL);
+ EXPECT_TRUE(first_timestamp != NULL);
+ EXPECT_TRUE(second_timestamp != NULL);
+
+ snprintf(buffer, sizeof(buffer), "%s -t '%s' 2>&1", cmd, first_timestamp);
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ int second_count = 0;
+ int last_timestamp_count = -1;
+
+ --count; // One less unless we match the first_timestamp
+ bool found = false;
+ while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ ++second_count;
+ // We want to highlight if we skip to the next entry.
+ // WAI, if the time in logd is *exactly*
+ // XX-XX XX:XX:XX.XXXXXX000 (usec) or XX-XX XX:XX:XX.XXX000000
+ // this can happen, but it should not happen with nsec.
+ // We can make this WAI behavior happen 1000 times less
+ // frequently if the caller does not use the -v usec flag,
+ // but always the second (always skip) if they use the
+ // (undocumented) -v nsec flag.
+ if (first_timestamp) {
+ found = !strcmp(input, first_timestamp);
+ if (found) {
+ ++count;
+ GTEST_LOG_(INFO)
+ << "input = first(" << first_timestamp << ")\n";
+ }
+ free(first_timestamp);
+ first_timestamp = NULL;
+ }
+ if (second_timestamp) {
+ found = found || !strcmp(input, second_timestamp);
+ if (!found) {
+ GTEST_LOG_(INFO) << "input(" << input << ") != second("
+ << second_timestamp << ")\n";
+ }
+ free(second_timestamp);
+ second_timestamp = NULL;
+ }
+ if (!strcmp(input, last_timestamp)) {
+ last_timestamp_count = second_count;
+ }
+ }
+ pclose(fp);
+
+ EXPECT_TRUE(found);
+ if (!found) {
+ if (first_timestamp) {
+ GTEST_LOG_(INFO) << "first = " << first_timestamp << "\n";
+ }
+ if (second_timestamp) {
+ GTEST_LOG_(INFO) << "second = " << second_timestamp << "\n";
+ }
+ if (last_timestamp) {
+ GTEST_LOG_(INFO) << "last = " << last_timestamp << "\n";
+ }
+ }
+ free(last_timestamp);
+ last_timestamp = NULL;
+ free(first_timestamp);
+ free(second_timestamp);
+
+ EXPECT_TRUE(first_timestamp == NULL);
+ EXPECT_TRUE(second_timestamp == NULL);
+ EXPECT_LE(count, second_count);
+ EXPECT_LE(count, last_timestamp_count);
+}
+
+TEST(logcat, tail_time) {
+ do_tail_time(logcat_executable " -v long -v nsec -b all");
+}
+
+TEST(logcat, tail_time_epoch) {
+ do_tail_time(logcat_executable " -v long -v nsec -v epoch -b all");
+}
+
+TEST(logcat, End_to_End) {
+ pid_t pid = getpid();
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ FILE* fp;
+ ASSERT_TRUE(NULL !=
+ (fp = popen(logcat_executable " -v brief -b events -t 100 2>/dev/null", "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int p;
+ unsigned long long t;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) ||
+ (p != pid)) {
+ continue;
+ }
+
+ log_time tx((const char*)&t);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(1, count);
+}
+
+TEST(logcat, End_to_End_multitude) {
+ pid_t pid = getpid();
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ FILE* fp[256]; // does this count as a multitude!
+ memset(fp, 0, sizeof(fp));
+ size_t num = 0;
+ do {
+ EXPECT_TRUE(NULL != (fp[num] = popen(logcat_executable " -v brief -b events -t 100", "r")));
+ if (!fp[num]) {
+ fprintf(stderr,
+ "WARNING: limiting to %zu simultaneous logcat operations\n",
+ num);
+ break;
+ }
+ } while (++num < sizeof(fp) / sizeof(fp[0]));
+
+ char buffer[BIG_BUFFER];
+
+ size_t count = 0;
+
+ for (size_t idx = 0; idx < sizeof(fp) / sizeof(fp[0]); ++idx) {
+ if (!fp[idx]) break;
+ while (fgets(buffer, sizeof(buffer), fp[idx])) {
+ int p;
+ unsigned long long t;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) ||
+ (p != pid)) {
+ continue;
+ }
+
+ log_time tx((const char*)&t);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ pclose(fp[idx]);
+ }
+
+ ASSERT_EQ(num, count);
+}
+
+static int get_groups(const char* cmd) {
+ FILE* fp;
+
+ // NB: crash log only available in user space
+ EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
+
+ if (fp == NULL) {
+ return 0;
+ }
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int size, consumed, max, payload;
+ char size_mult[4], consumed_mult[4];
+ long full_size, full_consumed;
+
+ size = consumed = max = payload = 0;
+ // NB: crash log can be very small, not hit a Kb of consumed space
+ // doubly lucky we are not including it.
+ EXPECT_EQ(6, sscanf(buffer,
+ "%*s ring buffer is %d %3s (%d %3s consumed),"
+ " max entry is %d B, max payload is %d B",
+ &size, size_mult, &consumed, consumed_mult, &max, &payload))
+ << "Parse error on: " << buffer;
+ full_size = size;
+ switch (size_mult[0]) {
+ case 'G':
+ full_size *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'M':
+ full_size *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'K':
+ full_size *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'B':
+ break;
+ default:
+ ADD_FAILURE() << "Parse error on multiplier: " << size_mult;
+ }
+ full_consumed = consumed;
+ switch (consumed_mult[0]) {
+ case 'G':
+ full_consumed *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'M':
+ full_consumed *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'K':
+ full_consumed *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'B':
+ break;
+ default:
+ ADD_FAILURE() << "Parse error on multiplier: " << consumed_mult;
+ }
+ EXPECT_GT((full_size * 9) / 4, full_consumed);
+ EXPECT_GT(full_size, max);
+ EXPECT_GT(max, payload);
+
+ if ((((full_size * 9) / 4) >= full_consumed) && (full_size > max) &&
+ (max > payload)) {
+ ++count;
+ }
+ }
+
+ pclose(fp);
+
+ return count;
+}
+
+TEST(logcat, get_size) {
+ ASSERT_EQ(4, get_groups(logcat_executable
+ " -v brief -b radio -b events -b system -b "
+ "main -g 2>/dev/null"));
+}
+
+// duplicate test for get_size, but use comma-separated list of buffers
+TEST(logcat, multiple_buffer) {
+ ASSERT_EQ(
+ 4, get_groups(logcat_executable
+ " -v brief -b radio,events,system,main -g 2>/dev/null"));
+}
+
+TEST(logcat, bad_buffer) {
+ ASSERT_EQ(0,
+ get_groups(
+ logcat_executable
+ " -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+}
+
+#ifndef logcat
+static void caught_blocking(int signum) {
+ unsigned long long v = 0xDEADBEEFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+ if (signum == 0) ++v;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking) {
+ FILE* fp;
+ unsigned long long v = 0xDEADBEEFA55F0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ v &= 0xFFFFFFFFFFFAFFFFULL;
+
+ ASSERT_TRUE(
+ NULL !=
+ (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -v brief -b events 2>&1",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+
+ int signals = 0;
+
+ signal(SIGALRM, caught_blocking);
+ alarm(2);
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(buffer, "DONE", 4)) {
+ break;
+ }
+
+ ++count;
+
+ int p;
+ unsigned long long l;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
+ continue;
+ }
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ // Generate SIGPIPE
+ fclose(fp);
+ caught_blocking(0);
+
+ pclose(fp);
+
+ EXPECT_GE(count, 2);
+
+ EXPECT_EQ(signals, 1);
+}
+
+static void caught_blocking_tail(int signum) {
+ unsigned long long v = 0xA55ADEADBEEF0000ULL;
+
+ v += getpid() & 0xFFFF;
+ if (signum == 0) ++v;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking_tail) {
+ FILE* fp;
+ unsigned long long v = 0xA55FDEADBEEF0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ v &= 0xFFFAFFFFFFFFFFFFULL;
+
+ ASSERT_TRUE(
+ NULL !=
+ (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -v brief -b events -T 5 2>&1",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+
+ int signals = 0;
+
+ signal(SIGALRM, caught_blocking_tail);
+ alarm(2);
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(buffer, "DONE", 4)) {
+ break;
+ }
+
+ ++count;
+
+ int p;
+ unsigned long long l;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
+ continue;
+ }
+
+ if (l == v) {
+ if (count >= 5) {
+ ++signals;
+ }
+ break;
+ }
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ // Generate SIGPIPE
+ fclose(fp);
+ caught_blocking_tail(0);
+
+ pclose(fp);
+
+ EXPECT_GE(count, 2);
+
+ EXPECT_EQ(signals, 1);
+}
+#endif
+
+// meant to be handed to ASSERT_FALSE / EXPECT_FALSE to expand the message
+static testing::AssertionResult IsFalse(int ret, const char* command) {
+ return ret ? (testing::AssertionSuccess()
+ << "ret=" << ret << " command=\"" << command << "\"")
+ : testing::AssertionFailure();
+}
+
+TEST(logcat, logrotate) {
+ static const char form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char buf[sizeof(form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
+
+ static const char comm[] = logcat_executable
+ " -b radio -b events -b system -b main"
+ " -d -f %s/log.txt -n 7 -r 1";
+ char command[sizeof(buf) + sizeof(comm)];
+ snprintf(command, sizeof(command), comm, buf);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (!ret) {
+ snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
+
+ FILE* fp;
+ EXPECT_TRUE(NULL != (fp = popen(command, "r")));
+ if (fp) {
+ char buffer[BIG_BUFFER];
+ int count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ static const char total[] = "total ";
+ int num;
+ char c;
+
+ if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
+ (num <= 40)) {
+ ++count;
+ } else if (strncmp(buffer, total, sizeof(total) - 1)) {
+ fprintf(stderr, "WARNING: Parse error: %s", buffer);
+ }
+ }
+ pclose(fp);
+ if ((count != 7) && (count != 8)) {
+ fprintf(stderr, "count=%d\n", count);
+ }
+ EXPECT_TRUE(count == 7 || count == 8);
+ }
+ }
+ snprintf(command, sizeof(command), "rm -rf %s", buf);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+TEST(logcat, logrotate_suffix) {
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char logcat_cmd[] = logcat_executable
+ " -b radio -b events -b system -b main"
+ " -d -f %s/log.txt -n 10 -r 1";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (!ret) {
+ snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
+
+ FILE* fp;
+ EXPECT_TRUE(NULL != (fp = popen(command, "r")));
+ char buffer[BIG_BUFFER];
+ int log_file_count = 0;
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ static const char rotated_log_filename_prefix[] = "log.txt.";
+ static const size_t rotated_log_filename_prefix_len =
+ strlen(rotated_log_filename_prefix);
+ static const char log_filename[] = "log.txt";
+
+ if (!strncmp(buffer, rotated_log_filename_prefix,
+ rotated_log_filename_prefix_len)) {
+ // Rotated file should have form log.txt.##
+ char* rotated_log_filename_suffix =
+ buffer + rotated_log_filename_prefix_len;
+ char* endptr;
+ const long int suffix_value =
+ strtol(rotated_log_filename_suffix, &endptr, 10);
+ EXPECT_EQ(rotated_log_filename_suffix + 2, endptr);
+ EXPECT_LE(suffix_value, 10);
+ EXPECT_GT(suffix_value, 0);
+ ++log_file_count;
+ continue;
+ }
+
+ if (!strncmp(buffer, log_filename, strlen(log_filename))) {
+ ++log_file_count;
+ continue;
+ }
+
+ fprintf(stderr, "ERROR: Unexpected file: %s", buffer);
+ ADD_FAILURE();
+ }
+ pclose(fp);
+ EXPECT_EQ(log_file_count, 11);
+ }
+ snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+TEST(logcat, logrotate_continue) {
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char log_filename[] = "log.txt";
+ static const char logcat_cmd[] =
+ logcat_executable " -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ FILE* fp;
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ char* line = NULL;
+ char* last_line =
+ NULL; // this line is allowed to stutter, one-line overlap
+ char* second_last_line = NULL; // should never stutter
+ char* first_line = NULL; // help diagnose failure?
+ size_t len = 0;
+ while (getline(&line, &len, fp) != -1) {
+ if (!first_line) {
+ first_line = line;
+ line = NULL;
+ continue;
+ }
+ free(second_last_line);
+ second_last_line = last_line;
+ last_line = line;
+ line = NULL;
+ }
+ fclose(fp);
+ free(line);
+ if (second_last_line == NULL) {
+ fprintf(stderr, "No second to last line, using last, test may fail\n");
+ second_last_line = last_line;
+ last_line = NULL;
+ }
+ free(last_line);
+ EXPECT_TRUE(NULL != second_last_line);
+ if (!second_last_line) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ free(first_line);
+ return;
+ }
+ // re-run the command, it should only add a few lines more content if it
+ // continues where it left off.
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ free(second_last_line);
+ free(first_line);
+ return;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ free(second_last_line);
+ free(first_line);
+ return;
+ }
+ struct dirent* entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ fprintf(stderr, "%s ?\n", command);
+ continue;
+ }
+ line = NULL;
+ size_t number = 0;
+ while (getline(&line, &len, fp) != -1) {
+ ++number;
+ if (!strcmp(line, second_last_line)) {
+ EXPECT_TRUE(++count <= 1);
+ fprintf(stderr, "%s(%zu):\n", entry->d_name, number);
+ }
+ }
+ fclose(fp);
+ free(line);
+ unlink(command);
+ }
+ if (count > 1) {
+ char* brk = strpbrk(second_last_line, "\r\n");
+ if (!brk) brk = second_last_line + strlen(second_last_line);
+ fprintf(stderr, "\"%.*s\" occurred %u times\n",
+ (int)(brk - second_last_line), second_last_line, count);
+ if (first_line) {
+ brk = strpbrk(first_line, "\r\n");
+ if (!brk) brk = first_line + strlen(first_line);
+ fprintf(stderr, "\"%.*s\" was first line, fault?\n",
+ (int)(brk - first_line), first_line);
+ }
+ }
+ free(second_last_line);
+ free(first_line);
+
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+TEST(logcat, logrotate_clear) {
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char log_filename[] = "log.txt";
+ static const unsigned num_val = 32;
+ static const char logcat_cmd[] =
+ logcat_executable " -b all -d -f %s/%s -n %d -r 1";
+ static const char clear_cmd[] = " -c";
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
+ sizeof(log_filename) + sizeof(clear_cmd) + 32];
+
+ // Run command with all data
+ {
+ snprintf(command, sizeof(command) - sizeof(clear_cmd), logcat_cmd,
+ tmp_out_dir, log_filename, num_val);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ struct dirent* entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ ++count;
+ }
+ EXPECT_EQ(count, num_val + 1);
+ }
+
+ {
+ // Now with -c option tacked onto the end
+ strcat(command, clear_cmd);
+
+ int ret;
+ EXPECT_FALSE(IsFalse(ret = system(command), command));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
+ EXPECT_NE(nullptr, dir);
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+ return;
+ }
+ struct dirent* entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ fprintf(stderr, "Found %s/%s!!!\n", tmp_out_dir, entry->d_name);
+ ++count;
+ }
+ EXPECT_EQ(count, 0U);
+ }
+
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+static int logrotate_count_id(const char* logcat_cmd, const char* tmp_out_dir) {
+ static const char log_filename[] = "log.txt";
+ char command[strlen(tmp_out_dir) + strlen(logcat_cmd) +
+ strlen(log_filename) + 32];
+
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+ int ret = system(command);
+ if (ret) {
+ fprintf(stderr, "system(\"%s\")=%d", command, ret);
+ return -1;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir),
+ closedir);
+ if (!dir) {
+ fprintf(stderr, "opendir(\"%s\") failed", tmp_out_dir);
+ return -1;
+ }
+ struct dirent* entry;
+ int count = 0;
+ while ((entry = readdir(dir.get()))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ ++count;
+ }
+ return count;
+}
+
+TEST(logcat, logrotate_id) {
+ static const char logcat_cmd[] =
+ logcat_executable " -b all -d -f %s/%s -n 32 -r 1 --id=test";
+ static const char logcat_short_cmd[] =
+ logcat_executable " -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+ static const char tmp_out_dir_form[] =
+ "/data/local/tmp/logcat.logrotate.XXXXXX";
+ static const char log_filename[] = "log.txt";
+ char tmp_out_dir[strlen(tmp_out_dir_form) + 1];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ EXPECT_EQ(logrotate_count_id(logcat_cmd, tmp_out_dir), 34);
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
+
+ char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5];
+ snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename);
+ if (getuid() != 0) {
+ chmod(id_file, 0);
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
+ }
+ unlink(id_file);
+ EXPECT_EQ(logrotate_count_id(logcat_short_cmd, tmp_out_dir), 34);
+
+ FILE* fp = fopen(id_file, "w");
+ if (fp) {
+ fprintf(fp, "not_a_test");
+ fclose(fp);
+ }
+ if (getuid() != 0) {
+ chmod(id_file,
+ 0); // API to preserve content even with signature change
+ ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir));
+ chmod(id_file, 0600);
+ }
+
+ int new_signature;
+ EXPECT_GE(
+ (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir)), 2);
+ EXPECT_LT(new_signature, 34);
+
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)];
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(IsFalse(system(command), command));
+}
+
+TEST(logcat, logrotate_nodir) {
+ // expect logcat to error out on writing content and not exit(0) for nodir
+ static const char command[] = logcat_executable
+ " -b all -d"
+ " -f /das/nein/gerfingerpoken/logcat/log.txt"
+ " -n 256 -r 1024";
+ EXPECT_FALSE(IsFalse(0 == system(command), command));
+}
+
+#ifndef logcat
+static void caught_blocking_clear(int signum) {
+ unsigned long long v = 0xDEADBEEFA55C0000ULL;
+
+ v += getpid() & 0xFFFF;
+ if (signum == 0) ++v;
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking_clear) {
+ FILE* fp;
+ unsigned long long v = 0xDEADBEEFA55C0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ // This test is racey; an event occurs between clear and dump.
+ // We accept that we will get a false positive, but never a false negative.
+ ASSERT_TRUE(
+ NULL !=
+ (fp = popen("( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+ " logcat -b events -c 2>&1 ;"
+ " logcat -b events -g 2>&1 ;"
+ " logcat -v brief -b events 2>&1",
+ "r")));
+
+ char buffer[BIG_BUFFER];
+
+ int count = 0;
+ int minus_g = 0;
+
+ int signals = 0;
+
+ signal(SIGALRM, caught_blocking_clear);
+ alarm(2);
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(buffer, "clearLog: ", strlen("clearLog: "))) {
+ fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+ count = signals = 1;
+ break;
+ }
+ if (!strncmp(buffer, "failed to clear", strlen("failed to clear"))) {
+ fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+ count = signals = 1;
+ break;
+ }
+
+ if (!strncmp(buffer, "DONE", 4)) {
+ break;
+ }
+
+ int size, consumed, max, payload;
+ char size_mult[4], consumed_mult[4];
+ size = consumed = max = payload = 0;
+ if (6 == sscanf(buffer,
+ "events: ring buffer is %d %3s (%d %3s consumed),"
+ " max entry is %d B, max payload is %d B",
+ &size, size_mult, &consumed, consumed_mult, &max, &payload)) {
+ long full_size = size, full_consumed = consumed;
+
+ switch (size_mult[0]) {
+ case 'G':
+ full_size *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'M':
+ full_size *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'K':
+ full_size *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'B':
+ break;
+ }
+ switch (consumed_mult[0]) {
+ case 'G':
+ full_consumed *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'M':
+ full_consumed *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'K':
+ full_consumed *= 1024;
+ FALLTHROUGH_INTENDED;
+ case 'B':
+ break;
+ }
+ EXPECT_GT(full_size, full_consumed);
+ EXPECT_GT(full_size, max);
+ EXPECT_GT(max, payload);
+ EXPECT_GT(max, full_consumed);
+
+ ++minus_g;
+ continue;
+ }
+
+ ++count;
+
+ int p;
+ unsigned long long l;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) || (p != pid)) {
+ continue;
+ }
+
+ if (l == v) {
+ if (count > 1) {
+ fprintf(stderr, "WARNING: Possible false positive\n");
+ }
+ ++signals;
+ break;
+ }
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ // Generate SIGPIPE
+ fclose(fp);
+ caught_blocking_clear(0);
+
+ pclose(fp);
+
+ EXPECT_GE(count, 1);
+ EXPECT_EQ(minus_g, 1);
+
+ EXPECT_EQ(signals, 1);
+}
+#endif
+
+static bool get_white_black(char** list) {
+ FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
+ return false;
+ }
+
+ char buffer[BIG_BUFFER];
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char* hold = *list;
+ char* buf = buffer;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ char* end = buf + strlen(buf);
+ while (isspace(*--end) && (end >= buf)) {
+ *end = '\0';
+ }
+ if (end < buf) {
+ continue;
+ }
+ if (hold) {
+ asprintf(list, "%s %s", hold, buf);
+ free(hold);
+ } else {
+ asprintf(list, "%s", buf);
+ }
+ }
+ pclose(fp);
+ return *list != NULL;
+}
+
+static bool set_white_black(const char* list) {
+ char buffer[BIG_BUFFER];
+ snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
+ list ? list : "");
+ FILE* fp = popen(buffer, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: %s\n", buffer);
+ return false;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char* buf = buffer;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ char* end = buf + strlen(buf);
+ while ((end > buf) && isspace(*--end)) {
+ *end = '\0';
+ }
+ if (end <= buf) {
+ continue;
+ }
+ fprintf(stderr, "%s\n", buf);
+ pclose(fp);
+ return false;
+ }
+ return pclose(fp) == 0;
+}
+
+TEST(logcat, white_black_adjust) {
+ char* list = NULL;
+ char* adjust = NULL;
+
+ get_white_black(&list);
+
+ static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30";
+ ASSERT_EQ(true, set_white_black(adjustment));
+ ASSERT_EQ(true, get_white_black(&adjust));
+ EXPECT_STREQ(adjustment, adjust);
+ free(adjust);
+ adjust = NULL;
+
+ static const char adjustment2[] = "300/20 300/21 2000 ~1000";
+ ASSERT_EQ(true, set_white_black(adjustment2));
+ ASSERT_EQ(true, get_white_black(&adjust));
+ EXPECT_STREQ(adjustment2, adjust);
+ free(adjust);
+ adjust = NULL;
+
+ ASSERT_EQ(true, set_white_black(list));
+ get_white_black(&adjust);
+ EXPECT_STREQ(list ? list : "", adjust ? adjust : "");
+ free(adjust);
+ adjust = NULL;
+
+ free(list);
+ list = NULL;
+}
+
+TEST(logcat, regex) {
+ FILE* fp;
+ int count = 0;
+
+ char buffer[BIG_BUFFER];
+#define logcat_regex_prefix logcat_executable "_test"
+
+ snprintf(buffer, sizeof(buffer),
+ logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
+ getpid());
+
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_ab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_b"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_aaaab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
+ logcat_regex_prefix "_aaaa"));
+ // Let the logs settle
+ rest();
+
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+
+ EXPECT_TRUE(strstr(buffer, logcat_regex_prefix "_") != NULL);
+
+ count++;
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(2, count);
+}
+
+TEST(logcat, maxcount) {
+ FILE* fp;
+ int count = 0;
+
+ char buffer[BIG_BUFFER];
+
+ snprintf(buffer, sizeof(buffer),
+ logcat_executable " --pid %d -d --max-count 3", getpid());
+
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(
+ __android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+
+ rest();
+
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+
+ count++;
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(3, count);
+}
+
+static bool End_to_End(const char* tag, const char* fmt, ...)
+#if defined(__GNUC__)
+ __attribute__((__format__(printf, 2, 3)))
+#endif
+ ;
+
+static bool End_to_End(const char* tag, const char* fmt, ...) {
+ FILE* fp = popen(logcat_executable " -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
+ if (!fp) {
+ fprintf(stderr, "End_to_End: popen failed");
+ return false;
+ }
+
+ char buffer[BIG_BUFFER];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+
+ char* str = NULL;
+ asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer);
+ std::string expect(str);
+ free(str);
+
+ int count = 0;
+ pid_t pid = getpid();
+ std::string lastMatch;
+ int maxMatch = 1;
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char space;
+ char newline;
+ int p;
+ int ret = sscanf(buffer, expect.c_str(), &p, &space, &newline);
+ if ((ret == 3) && (p == pid) && (space == ' ') && (newline == '\n')) {
+ ++count;
+ } else if ((ret >= maxMatch) && (p == pid) && (count == 0)) {
+ lastMatch = buffer;
+ maxMatch = ret;
+ }
+ }
+
+ pclose(fp);
+
+ if ((count == 0) && (lastMatch.length() > 0)) {
+ // Help us pinpoint where things went wrong ...
+ fprintf(stderr, "Closest match for\n %s\n is\n %s",
+ expect.c_str(), lastMatch.c_str());
+ } else if (count > 3) {
+ fprintf(stderr, "Too many matches (%d) for %s\n", count, expect.c_str());
+ }
+
+ // Three different known tests, we can see pollution from the others
+ return count && (count <= 3);
+}
+
+TEST(logcat, descriptive) {
+ struct tag {
+ uint32_t tagNo;
+ const char* tagStr;
+ };
+ int ret;
+
+ {
+ static const struct tag hhgtg = { 42, "answer" };
+ android_log_event_list ctx(hhgtg.tagNo);
+ static const char theAnswer[] = "what is five by seven";
+ ctx << theAnswer;
+ // crafted to rest at least once after, and rest between retries.
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(
+ End_to_End(hhgtg.tagStr, "to life the universe etc=%s", theAnswer));
+ }
+
+ {
+ static const struct tag sync = { 2720, "sync" };
+ static const char id[] = logcat_executable ".descriptive-sync";
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr,
+ "[id=%s,event=42,source=-1,account=0]", id));
+ }
+
+ // Partial match to description
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "[id=%s,event=43,-1,0]", id));
+ }
+
+ // Negative Test of End_to_End, ensure it is working
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ fprintf(stderr, "Expect a \"Closest match\" message\n");
+ EXPECT_FALSE(End_to_End(
+ sync.tagStr, "[id=%s,event=44,source=-1,account=0]", id));
+ }
+ }
+
+ {
+ static const struct tag sync = { 2747, "contacts_aggregation" };
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint64_t)30 << (int32_t)2;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(
+ End_to_End(sync.tagStr, "[aggregation time=30ms,count=2]"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint64_t)31570 << (int32_t)911;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(
+ End_to_End(sync.tagStr, "[aggregation time=31.57s,count=911]"));
+ }
+ }
+
+ {
+ static const struct tag sync = { 75000, "sqlite_mem_alarm_current" };
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)512;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3072;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)2097152;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)2097153;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)1073741824;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
+ }
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
+ }
+ }
+
+ {
+ static const struct tag sync = { 27501, "notification_panel_hidden" };
+ android_log_event_list ctx(sync.tagNo);
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, ""));
+ }
+
+ {
+ // Invent new entries because existing can not serve
+ EventTagMap* map = android_openEventTagMap(nullptr);
+ ASSERT_TRUE(nullptr != map);
+ static const char name[] = logcat_executable ".descriptive-monotonic";
+ int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
+ ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ ASSERT_NE(-1, myTag);
+
+ const struct tag sync = { (uint32_t)myTag, name };
+
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)7;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)62;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)3673;
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
+ }
+ {
+ android_log_event_list ctx(sync.tagNo);
+ ctx << (uint32_t)(86400 + 7200 + 180 + 58);
+ for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
+ EXPECT_GE(ret, 0);
+ EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
+ }
+ }
+}
+
+static bool reportedSecurity(const char* command) {
+ FILE* fp = popen(command, "r");
+ if (!fp) return true;
+
+ std::string ret;
+ bool val = android::base::ReadFdToString(fileno(fp), &ret);
+ pclose(fp);
+
+ if (!val) return true;
+ return std::string::npos != ret.find("'security'");
+}
+
+TEST(logcat, security) {
+ EXPECT_FALSE(reportedSecurity(logcat_executable " -b all -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -c 2>&1"));
+ EXPECT_TRUE(
+ reportedSecurity(logcat_executable " -b security -G 256K 2>&1"));
+}
+
+static size_t commandOutputSize(const char* command) {
+ FILE* fp = popen(command, "r");
+ if (!fp) return 0;
+
+ std::string ret;
+ if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
+ if (pclose(fp) != 0) return 0;
+
+ return ret.size();
+}
+
+TEST(logcat, help) {
+ size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
+ EXPECT_GT(logcatHelpTextSize, 4096UL);
+ size_t logcatLastHelpTextSize =
+ commandOutputSize(logcat_executable " -L -h 2>&1");
+#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT // logcat and liblogcat
+ EXPECT_EQ(logcatHelpTextSize, logcatLastHelpTextSize);
+#else
+ // logcatd -L -h prints the help twice, as designed.
+ EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
+#endif
+}
+
+TEST(logcat, invalid_buffer) {
+ FILE* fp = popen("logcat -b foo 2>&1", "r");
+ ASSERT_NE(nullptr, fp);
+ std::string output;
+ ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &output));
+ pclose(fp);
+
+ ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
+}
diff --git a/logcat/tests/logcatd_test.cpp b/logcat/tests/logcatd_test.cpp
new file mode 100644
index 000000000..bb7534e10
--- /dev/null
+++ b/logcat/tests/logcatd_test.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define logcat logcatd
+#define logcat_executable "logcatd"
+
+#include "logcat_test.cpp"
diff --git a/logd/.clang-format b/logd/.clang-format
new file mode 120000
index 000000000..1af4f51dd
--- /dev/null
+++ b/logd/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4 \ No newline at end of file
diff --git a/logd/Android.bp b/logd/Android.bp
new file mode 100644
index 000000000..b337b7c2a
--- /dev/null
+++ b/logd/Android.bp
@@ -0,0 +1,105 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This is what we want to do:
+// event_logtags = $(shell
+// sed -n
+// "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p"
+// $(LOCAL_PATH)/$2/event.logtags)
+// event_flag := $(call event_logtags,auditd)
+// event_flag += $(call event_logtags,logd)
+// event_flag += $(call event_logtags,tag_def)
+// so make sure we do not regret hard-coding it as follows:
+event_flag = [
+ "-DAUDITD_LOG_TAG=1003",
+ "-DCHATTY_LOG_TAG=1004",
+ "-DTAG_DEF_LOG_TAG=1005",
+ "-DLIBLOG_LOG_TAG=1006",
+]
+
+cc_library_static {
+ name: "liblogd",
+
+ srcs: [
+ "LogCommand.cpp",
+ "CommandListener.cpp",
+ "LogListener.cpp",
+ "LogReader.cpp",
+ "FlushCommand.cpp",
+ "LogBuffer.cpp",
+ "LogBufferElement.cpp",
+ "LogTimes.cpp",
+ "LogStatistics.cpp",
+ "LogWhiteBlackList.cpp",
+ "libaudit.c",
+ "LogAudit.cpp",
+ "LogKlog.cpp",
+ "LogTags.cpp",
+ ],
+ logtags: ["event.logtags"],
+
+ shared_libs: ["libbase"],
+
+ export_include_dirs: ["."],
+
+ cflags: ["-Werror"] + event_flag,
+}
+
+cc_binary {
+ name: "logd",
+ init_rc: ["logd.rc"],
+
+ srcs: ["main.cpp"],
+
+ static_libs: [
+ "liblog",
+ "liblogd",
+ ],
+
+ shared_libs: [
+ "libsysutils",
+ "libcutils",
+ "libbase",
+ "libpackagelistparser",
+ "libprocessgroup",
+ "libcap",
+ ],
+
+ cflags: ["-Werror"],
+}
+
+cc_binary {
+ name: "auditctl",
+
+ srcs: ["auditctl.cpp"],
+
+ static_libs: [
+ "liblogd",
+ ],
+
+ shared_libs: ["libbase"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wconversion"
+ ],
+}
+
+prebuilt_etc {
+ name: "logtagd.rc",
+ src: "logtagd.rc",
+ sub_dir: "init",
+}
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
new file mode 100644
index 000000000..694b5fa3c
--- /dev/null
+++ b/logd/CommandListener.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "CommandListener.h"
+#include "LogCommand.h"
+#include "LogUtils.h"
+
+CommandListener::CommandListener(LogBuffer* buf, LogReader* /*reader*/,
+ LogListener* /*swl*/)
+ : FrameworkListener(getLogSocket()) {
+ // registerCmd(new ShutdownCmd(buf, writer, swl));
+ registerCmd(new ClearCmd(buf));
+ registerCmd(new GetBufSizeCmd(buf));
+ registerCmd(new SetBufSizeCmd(buf));
+ registerCmd(new GetBufSizeUsedCmd(buf));
+ registerCmd(new GetStatisticsCmd(buf));
+ registerCmd(new SetPruneListCmd(buf));
+ registerCmd(new GetPruneListCmd(buf));
+ registerCmd(new GetEventTagCmd(buf));
+ registerCmd(new ReinitCmd());
+ registerCmd(new ExitCmd(this));
+}
+
+CommandListener::ShutdownCmd::ShutdownCmd(LogReader* reader, LogListener* swl)
+ : LogCommand("shutdown"), mReader(*reader), mSwl(*swl) {
+}
+
+int CommandListener::ShutdownCmd::runCommand(SocketClient* /*cli*/,
+ int /*argc*/, char** /*argv*/) {
+ mSwl.stopListener();
+ mReader.stopListener();
+ exit(0);
+}
+
+CommandListener::ClearCmd::ClearCmd(LogBuffer* buf)
+ : LogCommand("clear"), mBuf(*buf) {
+}
+
+static void setname() {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.control");
+ name_set = true;
+ }
+}
+
+int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ cli->sendMsg(mBuf.clear((log_id_t)id, uid) ? "busy" : "success");
+ return 0;
+}
+
+CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer* buf)
+ : LogCommand("getLogSize"), mBuf(*buf) {
+}
+
+int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = mBuf.getSize((log_id_t)id);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%lu", size);
+ cli->sendMsg(buf);
+ return 0;
+}
+
+CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer* buf)
+ : LogCommand("setLogSize"), mBuf(*buf) {
+}
+
+int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ if (!clientHasLogCredentials(cli)) {
+ cli->sendMsg("Permission Denied");
+ return 0;
+ }
+
+ if (argc < 3) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = atol(argv[2]);
+ if (mBuf.setSize((log_id_t)id, size)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ cli->sendMsg("success");
+ return 0;
+}
+
+CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer* buf)
+ : LogCommand("getLogSizeUsed"), mBuf(*buf) {
+}
+
+int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = mBuf.getSizeUsed((log_id_t)id);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%lu", size);
+ cli->sendMsg(buf);
+ return 0;
+}
+
+CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer* buf)
+ : LogCommand("getStatistics"), mBuf(*buf) {
+}
+
+// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
+// prefix includes the length of the prefix itself.
+static std::string PackageString(const std::string& str) {
+ size_t overhead_length = 3; // \n \n \f.
+
+ // Number of digits needed to represent length(str + overhead_length).
+ size_t str_size_digits = 1 + static_cast<size_t>(log10(str.size() + overhead_length));
+ // Number of digits needed to represent the total size.
+ size_t total_size_digits =
+ 1 + static_cast<size_t>(log10(str.size() + overhead_length + str_size_digits));
+
+ // If adding the size prefix causes a new digit to be required to represent the new total
+ // size, add it to the 'overhead_length'. This can only happen once, since each new digit
+ // allows for 10x the previous size to be recorded.
+ if (total_size_digits != str_size_digits) {
+ overhead_length++;
+ }
+
+ size_t total_size = str.size() + overhead_length + str_size_digits;
+ return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
+}
+
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ unsigned int logMask = -1;
+ pid_t pid = 0;
+ if (argc > 1) {
+ logMask = 0;
+ for (int i = 1; i < argc; ++i) {
+ static const char _pid[] = "pid=";
+ if (!strncmp(argv[i], _pid, sizeof(_pid) - 1)) {
+ pid = atol(argv[i] + sizeof(_pid) - 1);
+ if (pid == 0) {
+ cli->sendMsg("PID Error");
+ return 0;
+ }
+ continue;
+ }
+
+ int id = atoi(argv[i]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+ logMask |= 1 << id;
+ }
+ }
+
+ cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+ return 0;
+}
+
+CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer* buf)
+ : LogCommand("getPruneList"), mBuf(*buf) {
+}
+
+int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
+ int /*argc*/, char** /*argv*/) {
+ setname();
+ cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
+ return 0;
+}
+
+CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer* buf)
+ : LogCommand("setPruneList"), mBuf(*buf) {
+}
+
+int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ if (!clientHasLogCredentials(cli)) {
+ cli->sendMsg("Permission Denied");
+ return 0;
+ }
+
+ std::string str;
+ for (int i = 1; i < argc; ++i) {
+ if (str.length()) {
+ str += " ";
+ }
+ str += argv[i];
+ }
+
+ int ret = mBuf.initPrune(str.c_str());
+
+ if (ret) {
+ cli->sendMsg("Invalid");
+ return 0;
+ }
+
+ cli->sendMsg("success");
+
+ return 0;
+}
+
+CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer* buf)
+ : LogCommand("getEventTag"), mBuf(*buf) {
+}
+
+int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
+ char** argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ const char* name = nullptr;
+ const char* format = nullptr;
+ const char* id = nullptr;
+ for (int i = 1; i < argc; ++i) {
+ static const char _name[] = "name=";
+ if (!strncmp(argv[i], _name, strlen(_name))) {
+ name = argv[i] + strlen(_name);
+ continue;
+ }
+
+ static const char _format[] = "format=";
+ if (!strncmp(argv[i], _format, strlen(_format))) {
+ format = argv[i] + strlen(_format);
+ continue;
+ }
+
+ static const char _id[] = "id=";
+ if (!strncmp(argv[i], _id, strlen(_id))) {
+ id = argv[i] + strlen(_id);
+ continue;
+ }
+ }
+
+ if (id) {
+ if (format || name) {
+ cli->sendMsg("can not mix id= with either format= or name=");
+ return 0;
+ }
+ cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
+ return 0;
+ }
+
+ cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
+
+ return 0;
+}
+
+CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
+}
+
+int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
+ char** /*argv*/) {
+ setname();
+
+ reinit_signal_handler(SIGHUP);
+
+ cli->sendMsg("success");
+
+ return 0;
+}
+
+CommandListener::ExitCmd::ExitCmd(CommandListener* parent)
+ : LogCommand("EXIT"), mParent(*parent) {
+}
+
+int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
+ char** /*argv*/) {
+ setname();
+
+ cli->sendMsg("success");
+ release(cli);
+
+ return 0;
+}
+
+int CommandListener::getLogSocket() {
+ static const char socketName[] = "logd";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) {
+ sock = socket_local_server(
+ socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ }
+
+ return sock;
+}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
new file mode 100644
index 000000000..ed9941938
--- /dev/null
+++ b/logd/CommandListener.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _COMMANDLISTENER_H__
+#define _COMMANDLISTENER_H__
+
+#include <sysutils/FrameworkListener.h>
+#include "LogBuffer.h"
+#include "LogCommand.h"
+#include "LogListener.h"
+#include "LogReader.h"
+
+// See main.cpp for implementation
+void reinit_signal_handler(int /*signal*/);
+
+class CommandListener : public FrameworkListener {
+ public:
+ CommandListener(LogBuffer* buf, LogReader* reader, LogListener* swl);
+ virtual ~CommandListener() {
+ }
+
+ private:
+ static int getLogSocket();
+
+ class ShutdownCmd : public LogCommand {
+ LogReader& mReader;
+ LogListener& mSwl;
+
+ public:
+ ShutdownCmd(LogReader* reader, LogListener* swl);
+ virtual ~ShutdownCmd() {
+ }
+ int runCommand(SocketClient* c, int argc, char** argv);
+ };
+
+#define LogBufferCmd(name) \
+ class name##Cmd : public LogCommand { \
+ LogBuffer& mBuf; \
+ \
+ public: \
+ explicit name##Cmd(LogBuffer* buf); \
+ virtual ~name##Cmd() { \
+ } \
+ int runCommand(SocketClient* c, int argc, char** argv); \
+ }
+
+ LogBufferCmd(Clear);
+ LogBufferCmd(GetBufSize);
+ LogBufferCmd(SetBufSize);
+ LogBufferCmd(GetBufSizeUsed);
+ LogBufferCmd(GetStatistics);
+ LogBufferCmd(GetPruneList);
+ LogBufferCmd(SetPruneList);
+ LogBufferCmd(GetEventTag);
+
+#define LogCmd(name) \
+ class name##Cmd : public LogCommand { \
+ public: \
+ name##Cmd(); \
+ virtual ~name##Cmd() { \
+ } \
+ int runCommand(SocketClient* c, int argc, char** argv); \
+ }
+
+ LogCmd(Reinit);
+
+#define LogParentCmd(name) \
+ class name##Cmd : public LogCommand { \
+ CommandListener& mParent; \
+ \
+ public: \
+ name##Cmd(); \
+ explicit name##Cmd(CommandListener* parent); \
+ virtual ~name##Cmd() { \
+ } \
+ int runCommand(SocketClient* c, int argc, char** argv); \
+ void release(SocketClient* c) { \
+ mParent.release(c); \
+ } \
+ }
+
+ LogParentCmd(Exit);
+};
+
+#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
new file mode 100644
index 000000000..bd1755553
--- /dev/null
+++ b/logd/FlushCommand.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogCommand.h"
+#include "LogReader.h"
+#include "LogTimes.h"
+#include "LogUtils.h"
+
+// runSocketCommand is called once for every open client on the
+// log reader socket. Here we manage and associated the reader
+// client tracking and log region locks LastLogTimes list of
+// LogTimeEntrys, and spawn a transitory per-client thread to
+// work at filing data to the socket.
+//
+// global LogTimeEntry::wrlock() is used to protect access,
+// reference counts are used to ensure that individual
+// LogTimeEntry lifetime is managed when not protected.
+void FlushCommand::runSocketCommand(SocketClient* client) {
+ LogTimeEntry* entry = nullptr;
+ LastLogTimes& times = mReader.logbuf().mTimes;
+
+ LogTimeEntry::wrlock();
+ LastLogTimes::iterator it = times.begin();
+ while (it != times.end()) {
+ entry = it->get();
+ if (entry->mClient == client) {
+ if (!entry->isWatchingMultiple(mLogMask)) {
+ LogTimeEntry::unlock();
+ return;
+ }
+ if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
+ if (mReader.logbuf().isMonotonic()) {
+ LogTimeEntry::unlock();
+ return;
+ }
+ // If the user changes the time in a gross manner that
+ // invalidates the timeout, fall through and trigger.
+ log_time now(CLOCK_REALTIME);
+ if (((entry->mEnd + entry->mTimeout) > now) &&
+ (now > entry->mEnd)) {
+ LogTimeEntry::unlock();
+ return;
+ }
+ }
+ entry->triggerReader_Locked();
+ LogTimeEntry::unlock();
+ return;
+ }
+ it++;
+ }
+
+ LogTimeEntry::unlock();
+}
+
+bool FlushCommand::hasReadLogs(SocketClient* client) {
+ return clientHasLogCredentials(client);
+}
+
+static bool clientHasSecurityCredentials(SocketClient* client) {
+ return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
+}
+
+bool FlushCommand::hasSecurityLogs(SocketClient* client) {
+ return clientHasSecurityCredentials(client);
+}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
new file mode 100644
index 000000000..ceaf39362
--- /dev/null
+++ b/logd/FlushCommand.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _FLUSH_COMMAND_H
+#define _FLUSH_COMMAND_H
+
+#include <private/android_logger.h>
+#include <sysutils/SocketClientCommand.h>
+
+class LogBufferElement;
+
+#include "LogTimes.h"
+
+class LogReader;
+
+class FlushCommand : public SocketClientCommand {
+ LogReader& mReader;
+ log_mask_t mLogMask;
+
+ public:
+ explicit FlushCommand(LogReader& reader, log_mask_t logMask)
+ : mReader(reader), mLogMask(logMask) {
+ }
+
+ virtual void runSocketCommand(SocketClient* client);
+
+ static bool hasReadLogs(SocketClient* client);
+ static bool hasSecurityLogs(SocketClient* client);
+};
+
+#endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
new file mode 100644
index 000000000..d9cc0dba3
--- /dev/null
+++ b/logd/LogAudit.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+#include <syslog.h>
+
+#include <fstream>
+#include <sstream>
+
+#include <android-base/macros.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogAudit.h"
+#include "LogBuffer.h"
+#include "LogKlog.h"
+#include "LogReader.h"
+#include "LogUtils.h"
+#include "libaudit.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
+ '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
+
+LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
+ : SocketListener(getLogSocket(), false),
+ logbuf(buf),
+ reader(reader),
+ fdDmesg(fdDmesg),
+ main(__android_logger_property_get_bool("ro.logd.auditd.main",
+ BOOL_DEFAULT_TRUE)),
+ events(__android_logger_property_get_bool("ro.logd.auditd.events",
+ BOOL_DEFAULT_TRUE)),
+ initialized(false) {
+ static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
+ 'l',
+ 'o',
+ 'g',
+ 'd',
+ '.',
+ 'a',
+ 'u',
+ 'd',
+ 'i',
+ 't',
+ 'd',
+ ':',
+ ' ',
+ 's',
+ 't',
+ 'a',
+ 'r',
+ 't',
+ '\n' };
+ write(fdDmesg, auditd_message, sizeof(auditd_message));
+}
+
+bool LogAudit::onDataAvailable(SocketClient* cli) {
+ if (!initialized) {
+ prctl(PR_SET_NAME, "logd.auditd");
+ initialized = true;
+ }
+
+ struct audit_message rep;
+
+ rep.nlh.nlmsg_type = 0;
+ rep.nlh.nlmsg_len = 0;
+ rep.data[0] = '\0';
+
+ if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) {
+ SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));
+ return false;
+ }
+
+ logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+
+ return true;
+}
+
+static inline bool hasMetadata(char* str, int str_len) {
+ // need to check and see if str already contains bug metadata from
+ // possibility of stuttering if log audit crashes and then reloads kernel
+ // messages. Kernel denials that contain metadata will either end in
+ // "b/[0-9]+$" or "b/[0-9]+ duplicate messages suppressed$" which will put
+ // a '/' character at either 9 or 39 indices away from the end of the str.
+ return str_len >= 39 &&
+ (str[str_len - 9] == '/' || str[str_len - 39] == '/');
+}
+
+std::map<std::string, std::string> LogAudit::populateDenialMap() {
+ std::ifstream bug_file("/vendor/etc/selinux/selinux_denial_metadata");
+ std::string line;
+ // allocate a map for the static map pointer in auditParse to keep track of,
+ // this function only runs once
+ std::map<std::string, std::string> denial_to_bug;
+ if (bug_file.good()) {
+ std::string scontext;
+ std::string tcontext;
+ std::string tclass;
+ std::string bug_num;
+ while (std::getline(bug_file, line)) {
+ std::stringstream split_line(line);
+ split_line >> scontext >> tcontext >> tclass >> bug_num;
+ denial_to_bug.emplace(scontext + tcontext + tclass, bug_num);
+ }
+ }
+ return denial_to_bug;
+}
+
+std::string LogAudit::denialParse(const std::string& denial, char terminator,
+ const std::string& search_term) {
+ size_t start_index = denial.find(search_term);
+ if (start_index != std::string::npos) {
+ start_index += search_term.length();
+ return denial.substr(
+ start_index, denial.find(terminator, start_index) - start_index);
+ }
+ return "";
+}
+
+void LogAudit::auditParse(const std::string& string, uid_t uid,
+ std::string* bug_num) {
+ static std::map<std::string, std::string> denial_to_bug =
+ populateDenialMap();
+ std::string scontext = denialParse(string, ':', "scontext=u:object_r:");
+ std::string tcontext = denialParse(string, ':', "tcontext=u:object_r:");
+ std::string tclass = denialParse(string, ' ', "tclass=");
+ if (scontext.empty()) {
+ scontext = denialParse(string, ':', "scontext=u:r:");
+ }
+ if (tcontext.empty()) {
+ tcontext = denialParse(string, ':', "tcontext=u:r:");
+ }
+ auto search = denial_to_bug.find(scontext + tcontext + tclass);
+ if (search != denial_to_bug.end()) {
+ bug_num->assign(" " + search->second);
+ } else {
+ bug_num->assign("");
+ }
+
+ // Ensure the uid name is not null before passing it to the bug string.
+ if (uid >= AID_APP_START && uid <= AID_APP_END) {
+ char* uidname = android::uidToName(uid);
+ if (uidname) {
+ bug_num->append(" app=");
+ bug_num->append(uidname);
+ free(uidname);
+ }
+ }
+}
+
+int LogAudit::logPrint(const char* fmt, ...) {
+ if (fmt == nullptr) {
+ return -EINVAL;
+ }
+
+ va_list args;
+
+ char* str = nullptr;
+ va_start(args, fmt);
+ int rc = vasprintf(&str, fmt, args);
+ va_end(args);
+
+ if (rc < 0) {
+ return rc;
+ }
+ char* cp;
+ // Work around kernels missing
+ // https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
+ // Such kernels improperly add newlines inside audit messages.
+ while ((cp = strchr(str, '\n'))) {
+ *cp = ' ';
+ }
+
+ while ((cp = strstr(str, " "))) {
+ memmove(cp, cp + 1, strlen(cp + 1) + 1);
+ }
+ pid_t pid = getpid();
+ pid_t tid = gettid();
+ uid_t uid = AID_LOGD;
+ static const char pid_str[] = " pid=";
+ char* pidptr = strstr(str, pid_str);
+ if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+ cp = pidptr + sizeof(pid_str) - 1;
+ pid = 0;
+ while (isdigit(*cp)) {
+ pid = (pid * 10) + (*cp - '0');
+ ++cp;
+ }
+ tid = pid;
+ logbuf->wrlock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ memmove(pidptr, cp, strlen(cp) + 1);
+ }
+
+ bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+ static std::string denial_metadata;
+ if ((fdDmesg >= 0) && initialized) {
+ struct iovec iov[4];
+ static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
+ static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
+ static const char newline[] = "\n";
+
+ auditParse(str, uid, &denial_metadata);
+ iov[0].iov_base = info ? const_cast<char*>(log_info) : const_cast<char*>(log_warning);
+ iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+ iov[1].iov_base = str;
+ iov[1].iov_len = strlen(str);
+ iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+ iov[2].iov_len = denial_metadata.length();
+ iov[3].iov_base = const_cast<char*>(newline);
+ iov[3].iov_len = strlen(newline);
+
+ writev(fdDmesg, iov, arraysize(iov));
+ }
+
+ if (!main && !events) {
+ free(str);
+ return 0;
+ }
+
+ log_time now(log_time::EPOCH);
+
+ static const char audit_str[] = " audit(";
+ char* timeptr = strstr(str, audit_str);
+ if (timeptr &&
+ ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
+ (*cp == ':')) {
+ memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
+ memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
+ if (!isMonotonic()) {
+ if (android::isMonotonic(now)) {
+ LogKlog::convertMonotonicToReal(now);
+ }
+ } else {
+ if (!android::isMonotonic(now)) {
+ LogKlog::convertRealToMonotonic(now);
+ }
+ }
+ } else if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
+
+ // log to events
+
+ size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
+ if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
+ auditParse(str, uid, &denial_metadata);
+ str_len = (str_len + denial_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
+ ? str_len + denial_metadata.length()
+ : LOGGER_ENTRY_MAX_PAYLOAD;
+ size_t message_len = str_len + sizeof(android_log_event_string_t);
+
+ log_mask_t notify = 0;
+
+ if (events) { // begin scope for event buffer
+ uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
+
+ android_log_event_string_t* event =
+ reinterpret_cast<android_log_event_string_t*>(buffer);
+ event->header.tag = htole32(AUDITD_LOG_TAG);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(str_len);
+ memcpy(event->data, str, str_len - denial_metadata.length());
+ memcpy(event->data + str_len - denial_metadata.length(),
+ denial_metadata.c_str(), denial_metadata.length());
+
+ rc = logbuf->log(
+ LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+ (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+ if (rc >= 0) {
+ notify |= 1 << LOG_ID_EVENTS;
+ }
+ // end scope for event buffer
+ }
+
+ // log to main
+
+ static const char comm_str[] = " comm=\"";
+ const char* comm = strstr(str, comm_str);
+ const char* estr = str + strlen(str);
+ const char* commfree = nullptr;
+ if (comm) {
+ estr = comm;
+ comm += sizeof(comm_str) - 1;
+ } else if (pid == getpid()) {
+ pid = tid;
+ comm = "auditd";
+ } else {
+ logbuf->wrlock();
+ comm = commfree = logbuf->pidToName(pid);
+ logbuf->unlock();
+ if (!comm) {
+ comm = "unknown";
+ }
+ }
+
+ const char* ecomm = strchr(comm, '"');
+ if (ecomm) {
+ ++ecomm;
+ str_len = ecomm - comm;
+ } else {
+ str_len = strlen(comm) + 1;
+ ecomm = "";
+ }
+ size_t prefix_len = estr - str;
+ if (prefix_len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
+ message_len =
+ str_len + prefix_len + suffix_len + denial_metadata.length() + 2;
+
+ if (main) { // begin scope for main buffer
+ char newstr[message_len];
+
+ *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
+ strlcpy(newstr + 1, comm, str_len);
+ strncpy(newstr + 1 + str_len, str, prefix_len);
+ strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
+ strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
+ denial_metadata.c_str(), denial_metadata.length());
+
+ rc = logbuf->log(
+ LOG_ID_MAIN, now, uid, pid, tid, newstr,
+ (message_len <= UINT16_MAX) ? (uint16_t)message_len : UINT16_MAX);
+
+ if (rc >= 0) {
+ notify |= 1 << LOG_ID_MAIN;
+ }
+ // end scope for main buffer
+ }
+
+ free(const_cast<char*>(commfree));
+ free(str);
+
+ if (notify) {
+ reader->notifyNewLog(notify);
+ if (rc < 0) {
+ rc = message_len;
+ }
+ }
+
+ return rc;
+}
+
+int LogAudit::log(char* buf, size_t len) {
+ char* audit = strstr(buf, " audit(");
+ if (!audit || (audit >= &buf[len])) {
+ return 0;
+ }
+
+ *audit = '\0';
+
+ int rc;
+ char* type = strstr(buf, "type=");
+ if (type && (type < &buf[len])) {
+ rc = logPrint("%s %s", type, audit + 1);
+ } else {
+ rc = logPrint("%s", audit + 1);
+ }
+ *audit = ' ';
+ return rc;
+}
+
+int LogAudit::getLogSocket() {
+ int fd = audit_open();
+ if (fd < 0) {
+ return fd;
+ }
+ if (audit_setup(fd, getpid()) < 0) {
+ audit_close(fd);
+ fd = -1;
+ }
+ return fd;
+}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
new file mode 100644
index 000000000..c3d7a3ede
--- /dev/null
+++ b/logd/LogAudit.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_AUDIT_H__
+#define _LOGD_LOG_AUDIT_H__
+
+#include <map>
+
+#include <sysutils/SocketListener.h>
+
+#include "LogBuffer.h"
+
+class LogReader;
+
+class LogAudit : public SocketListener {
+ LogBuffer* logbuf;
+ LogReader* reader;
+ int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
+ bool main;
+ bool events;
+ bool initialized;
+
+ public:
+ LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
+ int log(char* buf, size_t len);
+ bool isMonotonic() {
+ return logbuf->isMonotonic();
+ }
+
+ protected:
+ virtual bool onDataAvailable(SocketClient* cli);
+
+ private:
+ static int getLogSocket();
+ std::map<std::string, std::string> populateDenialMap();
+ std::string denialParse(const std::string& denial, char terminator,
+ const std::string& search_term);
+ void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
+ int logPrint(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+};
+
+#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
new file mode 100644
index 000000000..1cf20617f
--- /dev/null
+++ b/logd/LogBuffer.cpp
@@ -0,0 +1,1225 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// for manual checking of stale entries during LogBuffer::erase()
+//#define DEBUG_CHECK_FOR_STALE_ENTRIES
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/user.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <unordered_map>
+
+#include <cutils/properties.h>
+#include <private/android_logger.h>
+
+#include "LogBuffer.h"
+#include "LogKlog.h"
+#include "LogReader.h"
+#include "LogUtils.h"
+
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
+// Default
+#define log_buffer_size(id) mMaxSize[id]
+
+const log_time LogBuffer::pruneMargin(3, 0);
+
+void LogBuffer::init() {
+ log_id_for_each(i) {
+ mLastSet[i] = false;
+ mLast[i] = mLogElements.begin();
+
+ if (setSize(i, __android_logger_get_buffer_size(i))) {
+ setSize(i, LOG_BUFFER_MIN_SIZE);
+ }
+ }
+ bool lastMonotonic = monotonic;
+ monotonic = android_log_clockid() == CLOCK_MONOTONIC;
+ if (lastMonotonic != monotonic) {
+ //
+ // Fixup all timestamps, may not be 100% accurate, but better than
+ // throwing what we have away when we get 'surprised' by a change.
+ // In-place element fixup so no need to check reader-lock. Entries
+ // should already be in timestamp order, but we could end up with a
+ // few out-of-order entries if new monotonics come in before we
+ // are notified of the reinit change in status. A Typical example would
+ // be:
+ // --------- beginning of system
+ // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data
+ // --------- beginning of kernel
+ // 0.000000 0 0 I : Initializing cgroup subsys
+ // as the act of mounting /data would trigger persist.logd.timestamp to
+ // be corrected. 1/30 corner case YMMV.
+ //
+ rdlock();
+ LogBufferElementCollection::iterator it = mLogElements.begin();
+ while ((it != mLogElements.end())) {
+ LogBufferElement* e = *it;
+ if (monotonic) {
+ if (!android::isMonotonic(e->mRealTime)) {
+ LogKlog::convertRealToMonotonic(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
+ }
+ } else {
+ if (android::isMonotonic(e->mRealTime)) {
+ LogKlog::convertMonotonicToReal(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
+ }
+ }
+ ++it;
+ }
+ unlock();
+ }
+
+ // We may have been triggered by a SIGHUP. Release any sleeping reader
+ // threads to dump their current content.
+ //
+ // NB: this is _not_ performed in the context of a SIGHUP, it is
+ // performed during startup, and in context of reinit administrative thread
+ LogTimeEntry::wrlock();
+
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry* entry = times->get();
+ entry->triggerReader_Locked();
+ times++;
+ }
+
+ LogTimeEntry::unlock();
+}
+
+LogBuffer::LogBuffer(LastLogTimes* times)
+ : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
+ pthread_rwlock_init(&mLogElementsLock, nullptr);
+
+ log_id_for_each(i) {
+ lastLoggedElements[i] = nullptr;
+ droppedElements[i] = nullptr;
+ }
+
+ init();
+}
+
+LogBuffer::~LogBuffer() {
+ log_id_for_each(i) {
+ delete lastLoggedElements[i];
+ delete droppedElements[i];
+ }
+}
+
+enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
+
+static enum match_type identical(LogBufferElement* elem,
+ LogBufferElement* last) {
+ // is it mostly identical?
+ // if (!elem) return DIFFERENT;
+ ssize_t lenl = elem->getMsgLen();
+ if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
+ // if (!last) return DIFFERENT;
+ ssize_t lenr = last->getMsgLen();
+ if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
+ // if (elem->getLogId() != last->getLogId()) return DIFFERENT;
+ if (elem->getUid() != last->getUid()) return DIFFERENT;
+ if (elem->getPid() != last->getPid()) return DIFFERENT;
+ if (elem->getTid() != last->getTid()) return DIFFERENT;
+
+ // last is more than a minute old, stop squashing identical messages
+ if (elem->getRealTime().nsec() >
+ (last->getRealTime().nsec() + 60 * NS_PER_SEC))
+ return DIFFERENT;
+
+ // Identical message
+ const char* msgl = elem->getMsg();
+ const char* msgr = last->getMsg();
+ if (lenl == lenr) {
+ if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+ // liblog tagged messages (content gets summed)
+ if ((elem->getLogId() == LOG_ID_EVENTS) &&
+ (lenl == sizeof(android_log_event_int_t)) &&
+ !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
+ sizeof(int32_t)) &&
+ (elem->getTag() == LIBLOG_LOG_TAG)) {
+ return SAME_LIBLOG;
+ }
+ }
+
+ // audit message (except sequence number) identical?
+ if (last->isBinary() &&
+ (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
+ (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
+ if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
+ sizeof(int32_t))) {
+ return DIFFERENT;
+ }
+ msgl += sizeof(android_log_event_string_t);
+ lenl -= sizeof(android_log_event_string_t);
+ msgr += sizeof(android_log_event_string_t);
+ lenr -= sizeof(android_log_event_string_t);
+ }
+ static const char avc[] = "): avc: ";
+ const char* avcl = android::strnstr(msgl, lenl, avc);
+ if (!avcl) return DIFFERENT;
+ lenl -= avcl - msgl;
+ const char* avcr = android::strnstr(msgr, lenr, avc);
+ if (!avcr) return DIFFERENT;
+ lenr -= avcr - msgr;
+ if (lenl != lenr) return DIFFERENT;
+ if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
+ lenl - strlen(avc))) {
+ return DIFFERENT;
+ }
+ return SAME;
+}
+
+int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, const char* msg, uint16_t len) {
+ if (log_id >= LOG_ID_MAX) {
+ return -EINVAL;
+ }
+
+ // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+ // This prevents any chance that an outside source can request an
+ // exact entry with time specified in ms or us precision.
+ if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
+ LogBufferElement* elem = new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
+
+ // b/137093665: don't coalesce security messages.
+ if (log_id == LOG_ID_SECURITY) {
+ wrlock();
+ log(elem);
+ unlock();
+
+ return len;
+ }
+
+ int prio = ANDROID_LOG_INFO;
+ const char* tag = nullptr;
+ size_t tag_len = 0;
+ if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
+ tag = tagToName(elem->getTag());
+ if (tag) {
+ tag_len = strlen(tag);
+ }
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ tag_len = strnlen(tag, len - 1);
+ }
+ if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
+ // Log traffic received to total
+ wrlock();
+ stats.addTotal(elem);
+ unlock();
+ delete elem;
+ return -EACCES;
+ }
+
+ wrlock();
+ LogBufferElement* currentLast = lastLoggedElements[log_id];
+ if (currentLast) {
+ LogBufferElement* dropped = droppedElements[log_id];
+ uint16_t count = dropped ? dropped->getDropped() : 0;
+ //
+ // State Init
+ // incoming:
+ // dropped = nullptr
+ // currentLast = nullptr;
+ // elem = incoming message
+ // outgoing:
+ // dropped = nullptr -> State 0
+ // currentLast = copy of elem
+ // log elem
+ // State 0
+ // incoming:
+ // count = 0
+ // dropped = nullptr
+ // currentLast = copy of last message
+ // elem = incoming message
+ // outgoing: if match != DIFFERENT
+ // dropped = copy of first identical message -> State 1
+ // currentLast = reference to elem
+ // break: if match == DIFFERENT
+ // dropped = nullptr -> State 0
+ // delete copy of last message (incoming currentLast)
+ // currentLast = copy of elem
+ // log elem
+ // State 1
+ // incoming:
+ // count = 0
+ // dropped = copy of first identical message
+ // currentLast = reference to last held-back incoming
+ // message
+ // elem = incoming message
+ // outgoing: if match == SAME
+ // delete copy of first identical message (dropped)
+ // dropped = reference to last held-back incoming
+ // message set to chatty count of 1 -> State 2
+ // currentLast = reference to elem
+ // outgoing: if match == SAME_LIBLOG
+ // dropped = copy of first identical message -> State 1
+ // take sum of currentLast and elem
+ // if sum overflows:
+ // log currentLast
+ // currentLast = reference to elem
+ // else
+ // delete currentLast
+ // currentLast = reference to elem, sum liblog.
+ // break: if match == DIFFERENT
+ // delete dropped
+ // dropped = nullptr -> State 0
+ // log reference to last held-back (currentLast)
+ // currentLast = copy of elem
+ // log elem
+ // State 2
+ // incoming:
+ // count = chatty count
+ // dropped = chatty message holding count
+ // currentLast = reference to last held-back incoming
+ // message.
+ // dropped = chatty message holding count
+ // elem = incoming message
+ // outgoing: if match != DIFFERENT
+ // delete chatty message holding count
+ // dropped = reference to last held-back incoming
+ // message, set to chatty count + 1
+ // currentLast = reference to elem
+ // break: if match == DIFFERENT
+ // log dropped (chatty message)
+ // dropped = nullptr -> State 0
+ // log reference to last held-back (currentLast)
+ // currentLast = copy of elem
+ // log elem
+ //
+ enum match_type match = identical(elem, currentLast);
+ if (match != DIFFERENT) {
+ if (dropped) {
+ // Sum up liblog tag messages?
+ if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
+ android_log_event_int_t* event =
+ reinterpret_cast<android_log_event_int_t*>(
+ const_cast<char*>(currentLast->getMsg()));
+ //
+ // To unit test, differentiate with something like:
+ // event->header.tag = htole32(CHATTY_LOG_TAG);
+ // here, then instead of delete currentLast below,
+ // log(currentLast) to see the incremental sums form.
+ //
+ uint32_t swab = event->payload.data;
+ unsigned long long total = htole32(swab);
+ event = reinterpret_cast<android_log_event_int_t*>(
+ const_cast<char*>(elem->getMsg()));
+ swab = event->payload.data;
+
+ lastLoggedElements[LOG_ID_EVENTS] = elem;
+ total += htole32(swab);
+ // check for overflow
+ if (total >= UINT32_MAX) {
+ log(currentLast);
+ unlock();
+ return len;
+ }
+ stats.addTotal(currentLast);
+ delete currentLast;
+ swab = total;
+ event->payload.data = htole32(swab);
+ unlock();
+ return len;
+ }
+ if (count == USHRT_MAX) {
+ log(dropped);
+ count = 1;
+ } else {
+ delete dropped;
+ ++count;
+ }
+ }
+ if (count) {
+ stats.addTotal(currentLast);
+ currentLast->setDropped(count);
+ }
+ droppedElements[log_id] = currentLast;
+ lastLoggedElements[log_id] = elem;
+ unlock();
+ return len;
+ }
+ if (dropped) { // State 1 or 2
+ if (count) { // State 2
+ log(dropped); // report chatty
+ } else { // State 1
+ delete dropped;
+ }
+ droppedElements[log_id] = nullptr;
+ log(currentLast); // report last message in the series
+ } else { // State 0
+ delete currentLast;
+ }
+ }
+ lastLoggedElements[log_id] = new LogBufferElement(*elem);
+
+ log(elem);
+ unlock();
+
+ return len;
+}
+
+// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
+void LogBuffer::log(LogBufferElement* elem) {
+ // cap on how far back we will sort in-place, otherwise append
+ static uint32_t too_far_back = 5; // five seconds
+ // Insert elements in time sorted order if possible
+ // NB: if end is region locked, place element at end of list
+ LogBufferElementCollection::iterator it = mLogElements.end();
+ LogBufferElementCollection::iterator last = it;
+ if (__predict_true(it != mLogElements.begin())) --it;
+ if (__predict_false(it == mLogElements.begin()) ||
+ __predict_true((*it)->getRealTime() <= elem->getRealTime()) ||
+ __predict_false((((*it)->getRealTime().tv_sec - too_far_back) >
+ elem->getRealTime().tv_sec) &&
+ (elem->getLogId() != LOG_ID_KERNEL) &&
+ ((*it)->getLogId() != LOG_ID_KERNEL))) {
+ mLogElements.push_back(elem);
+ } else {
+ log_time end(log_time::EPOCH);
+ bool end_set = false;
+ bool end_always = false;
+
+ LogTimeEntry::rdlock();
+
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry* entry = times->get();
+ if (!entry->mNonBlock) {
+ end_always = true;
+ break;
+ }
+ // it passing mEnd is blocked by the following checks.
+ if (!end_set || (end <= entry->mEnd)) {
+ end = entry->mEnd;
+ end_set = true;
+ }
+ times++;
+ }
+
+ if (end_always || (end_set && (end > (*it)->getRealTime()))) {
+ mLogElements.push_back(elem);
+ } else {
+ // should be short as timestamps are localized near end()
+ do {
+ last = it;
+ if (__predict_false(it == mLogElements.begin())) {
+ break;
+ }
+ --it;
+ } while (((*it)->getRealTime() > elem->getRealTime()) &&
+ (!end_set || (end <= (*it)->getRealTime())));
+ mLogElements.insert(last, elem);
+ }
+ LogTimeEntry::unlock();
+ }
+
+ stats.add(elem);
+ maybePrune(elem->getLogId());
+}
+
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
+//
+// LogBuffer::wrlock() must be held when this function is called.
+void LogBuffer::maybePrune(log_id_t id) {
+ size_t sizes = stats.sizes(id);
+ unsigned long maxSize = log_buffer_size(id);
+ if (sizes > maxSize) {
+ size_t sizeOver = sizes - ((maxSize * 9) / 10);
+ size_t elements = stats.realElements(id);
+ size_t minElements = elements / 100;
+ if (minElements < minPrune) {
+ minElements = minPrune;
+ }
+ unsigned long pruneRows = elements * sizeOver / sizes;
+ if (pruneRows < minElements) {
+ pruneRows = minElements;
+ }
+ if (pruneRows > maxPrune) {
+ pruneRows = maxPrune;
+ }
+ prune(id, pruneRows);
+ }
+}
+
+LogBufferElementCollection::iterator LogBuffer::erase(
+ LogBufferElementCollection::iterator it, bool coalesce) {
+ LogBufferElement* element = *it;
+ log_id_t id = element->getLogId();
+
+ // Remove iterator references in the various lists that will become stale
+ // after the element is erased from the main logging list.
+
+ { // start of scope for found iterator
+ int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
+ ? element->getTag()
+ : element->getUid();
+ LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
+ if ((found != mLastWorst[id].end()) && (it == found->second)) {
+ mLastWorst[id].erase(found);
+ }
+ }
+
+ { // start of scope for pid found iterator
+ // element->getUid() may not be AID_SYSTEM for next-best-watermark.
+ // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
+ // long term code stability, find() check should be fast for those ids.
+ LogBufferPidIteratorMap::iterator found =
+ mLastWorstPidOfSystem[id].find(element->getPid());
+ if ((found != mLastWorstPidOfSystem[id].end()) &&
+ (it == found->second)) {
+ mLastWorstPidOfSystem[id].erase(found);
+ }
+ }
+
+ bool setLast[LOG_ID_MAX];
+ bool doSetLast = false;
+ log_id_for_each(i) {
+ doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
+ }
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ LogBufferElementCollection::iterator bad = it;
+ int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
+ ? element->getTag()
+ : element->getUid();
+#endif
+ it = mLogElements.erase(it);
+ if (doSetLast) {
+ log_id_for_each(i) {
+ if (setLast[i]) {
+ if (__predict_false(it == mLogElements.end())) { // impossible
+ mLastSet[i] = false;
+ mLast[i] = mLogElements.begin();
+ } else {
+ mLast[i] = it; // push down the road as next-best-watermark
+ }
+ }
+ }
+ }
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+ log_id_for_each(i) {
+ for (auto b : mLastWorst[i]) {
+ if (bad == b.second) {
+ android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i,
+ b.first, key);
+ }
+ }
+ for (auto b : mLastWorstPidOfSystem[i]) {
+ if (bad == b.second) {
+ android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i,
+ b.first);
+ }
+ }
+ if (mLastSet[i] && (bad == mLast[i])) {
+ android::prdebug("stale mLast[%d]\n", i);
+ mLastSet[i] = false;
+ mLast[i] = mLogElements.begin();
+ }
+ }
+#endif
+ if (coalesce) {
+ stats.erase(element);
+ } else {
+ stats.subtract(element);
+ }
+ delete element;
+
+ return it;
+}
+
+// Define a temporary mechanism to report the last LogBufferElement pointer
+// for the specified uid, pid and tid. Used below to help merge-sort when
+// pruning for worst UID.
+class LogBufferElementKey {
+ const union {
+ struct {
+ uint32_t uid;
+ uint16_t pid;
+ uint16_t tid;
+ } __packed;
+ uint64_t value;
+ } __packed;
+
+ public:
+ LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid)
+ : uid(uid), pid(pid), tid(tid) {
+ }
+ explicit LogBufferElementKey(uint64_t key) : value(key) {
+ }
+
+ uint64_t getKey() {
+ return value;
+ }
+};
+
+class LogBufferElementLast {
+ typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
+ LogBufferElementMap map;
+
+ public:
+ bool coalesce(LogBufferElement* element, uint16_t dropped) {
+ LogBufferElementKey key(element->getUid(), element->getPid(),
+ element->getTid());
+ LogBufferElementMap::iterator it = map.find(key.getKey());
+ if (it != map.end()) {
+ LogBufferElement* found = it->second;
+ uint16_t moreDropped = found->getDropped();
+ if ((dropped + moreDropped) > USHRT_MAX) {
+ map.erase(it);
+ } else {
+ found->setDropped(dropped + moreDropped);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void add(LogBufferElement* element) {
+ LogBufferElementKey key(element->getUid(), element->getPid(),
+ element->getTid());
+ map[key.getKey()] = element;
+ }
+
+ inline void clear() {
+ map.clear();
+ }
+
+ void clear(LogBufferElement* element) {
+ log_time current =
+ element->getRealTime() - log_time(EXPIRE_RATELIMIT, 0);
+ for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
+ LogBufferElement* mapElement = it->second;
+ if ((mapElement->getDropped() >= EXPIRE_THRESHOLD) &&
+ (current > mapElement->getRealTime())) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+};
+
+// Determine if watermark is within pruneMargin + 1s from the end of the list,
+// the caller will use this result to set an internal busy flag indicating
+// the prune operation could not be completed because a reader is blocking
+// the request.
+bool LogBuffer::isBusy(log_time watermark) {
+ LogBufferElementCollection::iterator ei = mLogElements.end();
+ --ei;
+ return watermark < ((*ei)->getRealTime() - pruneMargin - log_time(1, 0));
+}
+
+// If the selected reader is blocking our pruning progress, decide on
+// what kind of mitigation is necessary to unblock the situation.
+void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
+ // A misbehaving or slow reader has its connection
+ // dropped if we hit too much memory pressure.
+ android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
+ me->mClient->getPid());
+ me->release_Locked();
+ } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ // Allow a blocked WRAP timeout reader to
+ // trigger and start reporting the log data.
+ me->triggerReader_Locked();
+ } else {
+ // tell slow reader to skip entries to catch up
+ android::prdebug(
+ "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
+ pruneRows, me->mClient->getPid());
+ me->triggerSkip_Locked(id, pruneRows);
+ }
+}
+
+// prune "pruneRows" of type "id" from the buffer.
+//
+// This garbage collection task is used to expire log entries. It is called to
+// remove all logs (clear), all UID logs (unprivileged clear), or every
+// 256 or 10% of the total logs (whichever is less) to prune the logs.
+//
+// First there is a prep phase where we discover the reader region lock that
+// acts as a backstop to any pruning activity to stop there and go no further.
+//
+// There are three major pruning loops that follow. All expire from the oldest
+// entries. Since there are multiple log buffers, the Android logging facility
+// will appear to drop entries 'in the middle' when looking at multiple log
+// sources and buffers. This effect is slightly more prominent when we prune
+// the worst offender by logging source. Thus the logs slowly loose content
+// and value as you move back in time. This is preferred since chatty sources
+// invariably move the logs value down faster as less chatty sources would be
+// expired in the noise.
+//
+// The first loop performs blacklisting and worst offender pruning. Falling
+// through when there are no notable worst offenders and have not hit the
+// region lock preventing further worst offender pruning. This loop also looks
+// after managing the chatty log entries and merging to help provide
+// statistical basis for blame. The chatty entries are not a notification of
+// how much logs you may have, but instead represent how much logs you would
+// have had in a virtual log buffer that is extended to cover all the in-memory
+// logs without loss. They last much longer than the represented pruned logs
+// since they get multiplied by the gains in the non-chatty log sources.
+//
+// The second loop get complicated because an algorithm of watermarks and
+// history is maintained to reduce the order and keep processing time
+// down to a minimum at scale. These algorithms can be costly in the face
+// of larger log buffers, or severly limited processing time granted to a
+// background task at lowest priority.
+//
+// This second loop does straight-up expiration from the end of the logs
+// (again, remember for the specified log buffer id) but does some whitelist
+// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
+// spam filtration all take priority. This second loop also checks if a region
+// lock is causing us to buffer too much in the logs to help the reader(s),
+// and will tell the slowest reader thread to skip log entries, and if
+// persistent and hits a further threshold, kill the reader thread.
+//
+// The third thread is optional, and only gets hit if there was a whitelist
+// and more needs to be pruned against the backstop of the region lock.
+//
+// LogBuffer::wrlock() must be held when this function is called.
+//
+bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+ LogTimeEntry* oldest = nullptr;
+ bool busy = false;
+ bool clearAll = pruneRows == ULONG_MAX;
+
+ LogTimeEntry::rdlock();
+
+ // Region locked?
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry* entry = times->get();
+ if (entry->isWatching(id) &&
+ (!oldest || (oldest->mStart > entry->mStart) ||
+ ((oldest->mStart == entry->mStart) &&
+ (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
+ oldest = entry;
+ }
+ times++;
+ }
+ log_time watermark(log_time::tv_sec_max, log_time::tv_nsec_max);
+ if (oldest) watermark = oldest->mStart - pruneMargin;
+
+ LogBufferElementCollection::iterator it;
+
+ if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
+ // Only here if clear all request from non system source, so chatty
+ // filter logistics is not required.
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ while (it != mLogElements.end()) {
+ LogBufferElement* element = *it;
+
+ if ((element->getLogId() != id) ||
+ (element->getUid() != caller_uid)) {
+ ++it;
+ continue;
+ }
+
+ if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ if (busy) kickMe(oldest, id, pruneRows);
+ break;
+ }
+
+ it = erase(it);
+ if (--pruneRows == 0) {
+ break;
+ }
+ }
+ LogTimeEntry::unlock();
+ return busy;
+ }
+
+ // prune by worst offenders; by blacklist, UID, and by PID of system UID
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
+ while (!clearAll && (pruneRows > 0)) {
+ // recalculate the worst offender on every batched pass
+ int worst = -1; // not valid for getUid() or getKey()
+ size_t worst_sizes = 0;
+ size_t second_worst_sizes = 0;
+ pid_t worstPid = 0; // POSIX guarantees PID != 0
+
+ if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+ // Calculate threshold as 12.5% of available storage
+ size_t threshold = log_buffer_size(id) / 8;
+
+ if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
+ stats.sortTags(AID_ROOT, (pid_t)0, 2, id)
+ .findWorst(worst, worst_sizes, second_worst_sizes,
+ threshold);
+ // per-pid filter for AID_SYSTEM sources is too complex
+ } else {
+ stats.sort(AID_ROOT, (pid_t)0, 2, id)
+ .findWorst(worst, worst_sizes, second_worst_sizes,
+ threshold);
+
+ if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+ stats.sortPids(worst, (pid_t)0, 2, id)
+ .findWorst(worstPid, worst_sizes, second_worst_sizes);
+ }
+ }
+ }
+
+ // skip if we have neither worst nor naughty filters
+ if ((worst == -1) && !hasBlacklist) {
+ break;
+ }
+
+ bool kick = false;
+ bool leading = true;
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ // Perform at least one mandatory garbage collection cycle in following
+ // - clear leading chatty tags
+ // - coalesce chatty tags
+ // - check age-out of preserved logs
+ bool gc = pruneRows <= 1;
+ if (!gc && (worst != -1)) {
+ { // begin scope for worst found iterator
+ LogBufferIteratorMap::iterator found =
+ mLastWorst[id].find(worst);
+ if ((found != mLastWorst[id].end()) &&
+ (found->second != mLogElements.end())) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ if (worstPid) { // begin scope for pid worst found iterator
+ // FYI: worstPid only set if !LOG_ID_EVENTS and
+ // !LOG_ID_SECURITY, not going to make that assumption ...
+ LogBufferPidIteratorMap::iterator found =
+ mLastWorstPidOfSystem[id].find(worstPid);
+ if ((found != mLastWorstPidOfSystem[id].end()) &&
+ (found->second != mLogElements.end())) {
+ leading = false;
+ it = found->second;
+ }
+ }
+ }
+ static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
+ LogBufferElementCollection::iterator lastt;
+ lastt = mLogElements.end();
+ --lastt;
+ LogBufferElementLast last;
+ while (it != mLogElements.end()) {
+ LogBufferElement* element = *it;
+
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ // Do not let chatty eliding trigger any reader mitigation
+ break;
+ }
+
+ if (element->getLogId() != id) {
+ ++it;
+ continue;
+ }
+ // below this point element->getLogId() == id
+
+ if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ uint16_t dropped = element->getDropped();
+
+ // remove any leading drops
+ if (leading && dropped) {
+ it = erase(it);
+ continue;
+ }
+
+ if (dropped && last.coalesce(element, dropped)) {
+ it = erase(it, true);
+ continue;
+ }
+
+ int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
+ ? element->getTag()
+ : element->getUid();
+
+ if (hasBlacklist && mPrune.naughty(element)) {
+ last.clear(element);
+ it = erase(it);
+ if (dropped) {
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ if (key == worst) {
+ kick = true;
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= element->getMsgLen();
+ }
+ continue;
+ }
+
+ if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old)) ||
+ (element->getRealTime() > (*lastt)->getRealTime())) {
+ break;
+ }
+
+ if (dropped) {
+ last.add(element);
+ if (worstPid &&
+ ((!gc && (element->getPid() == worstPid)) ||
+ (mLastWorstPidOfSystem[id].find(element->getPid()) ==
+ mLastWorstPidOfSystem[id].end()))) {
+ // element->getUid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not LOG_ID_EVENTS
+ // or LOG_ID_SECURITY because of worstPid check.
+ mLastWorstPidOfSystem[id][element->getPid()] = it;
+ }
+ if ((!gc && !worstPid && (key == worst)) ||
+ (mLastWorst[id].find(key) == mLastWorst[id].end())) {
+ mLastWorst[id][key] = it;
+ }
+ ++it;
+ continue;
+ }
+
+ if ((key != worst) ||
+ (worstPid && (element->getPid() != worstPid))) {
+ leading = false;
+ last.clear(element);
+ ++it;
+ continue;
+ }
+ // key == worst below here
+ // If worstPid set, then element->getPid() == worstPid below here
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ kick = true;
+
+ uint16_t len = element->getMsgLen();
+
+ // do not create any leading drops
+ if (leading) {
+ it = erase(it);
+ } else {
+ stats.drop(element);
+ element->setDropped(1);
+ if (last.coalesce(element, 1)) {
+ it = erase(it, true);
+ } else {
+ last.add(element);
+ if (worstPid &&
+ (!gc || (mLastWorstPidOfSystem[id].find(worstPid) ==
+ mLastWorstPidOfSystem[id].end()))) {
+ // element->getUid() may not be AID_SYSTEM, next best
+ // watermark if current one empty. id is not
+ // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
+ mLastWorstPidOfSystem[id][worstPid] = it;
+ }
+ if ((!gc && !worstPid) ||
+ (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
+ mLastWorst[id][worst] = it;
+ }
+ ++it;
+ }
+ }
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= len;
+ }
+ last.clear();
+
+ if (!kick || !mPrune.worstUidEnabled()) {
+ break; // the following loop will ask bad clients to skip/drop
+ }
+ }
+
+ bool whitelist = false;
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ while ((pruneRows > 0) && (it != mLogElements.end())) {
+ LogBufferElement* element = *it;
+
+ if (element->getLogId() != id) {
+ it++;
+ continue;
+ }
+
+ if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ if (!whitelist && busy) kickMe(oldest, id, pruneRows);
+ break;
+ }
+
+ if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+ // WhiteListed
+ whitelist = true;
+ it++;
+ continue;
+ }
+
+ it = erase(it);
+ pruneRows--;
+ }
+
+ // Do not save the whitelist if we are reader range limited
+ if (whitelist && (pruneRows > 0)) {
+ it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ while ((it != mLogElements.end()) && (pruneRows > 0)) {
+ LogBufferElement* element = *it;
+
+ if (element->getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
+ mLast[id] = it;
+ mLastSet[id] = true;
+ }
+
+ if (oldest && (watermark <= element->getRealTime())) {
+ busy = isBusy(watermark);
+ if (busy) kickMe(oldest, id, pruneRows);
+ break;
+ }
+
+ it = erase(it);
+ pruneRows--;
+ }
+ }
+
+ LogTimeEntry::unlock();
+
+ return (pruneRows > 0) && busy;
+}
+
+// clear all rows of type "id" from the buffer.
+bool LogBuffer::clear(log_id_t id, uid_t uid) {
+ bool busy = true;
+ // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+ for (int retry = 4;;) {
+ if (retry == 1) { // last pass
+ // Check if it is still busy after the sleep, we say prune
+ // one entry, not another clear run, so we are looking for
+ // the quick side effect of the return value to tell us if
+ // we have a _blocked_ reader.
+ wrlock();
+ busy = prune(id, 1, uid);
+ unlock();
+ // It is still busy, blocked reader(s), lets kill them all!
+ // otherwise, lets be a good citizen and preserve the slow
+ // readers and let the clear run (below) deal with determining
+ // if we are still blocked and return an error code to caller.
+ if (busy) {
+ LogTimeEntry::wrlock();
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry* entry = times->get();
+ // Killer punch
+ if (entry->isWatching(id)) {
+ android::prdebug(
+ "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
+ entry->mClient->getPid());
+ entry->release_Locked();
+ }
+ times++;
+ }
+ LogTimeEntry::unlock();
+ }
+ }
+ wrlock();
+ busy = prune(id, ULONG_MAX, uid);
+ unlock();
+ if (!busy || !--retry) {
+ break;
+ }
+ sleep(1); // Let reader(s) catch up after notification
+ }
+ return busy;
+}
+
+// get the used space associated with "id".
+unsigned long LogBuffer::getSizeUsed(log_id_t id) {
+ rdlock();
+ size_t retval = stats.sizes(id);
+ unlock();
+ return retval;
+}
+
+// set the total space allocated to "id"
+int LogBuffer::setSize(log_id_t id, unsigned long size) {
+ // Reasonable limits ...
+ if (!__android_logger_valid_buffer_size(size)) {
+ return -1;
+ }
+ wrlock();
+ log_buffer_size(id) = size;
+ unlock();
+ return 0;
+}
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t id) {
+ rdlock();
+ size_t retval = log_buffer_size(id);
+ unlock();
+ return retval;
+}
+
+log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
+ pid_t* lastTid, bool privileged, bool security,
+ int (*filter)(const LogBufferElement* element,
+ void* arg),
+ void* arg) {
+ LogBufferElementCollection::iterator it;
+ uid_t uid = reader->getUid();
+
+ rdlock();
+
+ if (start == log_time::EPOCH) {
+ // client wants to start from the beginning
+ it = mLogElements.begin();
+ } else {
+ // Cap to 300 iterations we look back for out-of-order entries.
+ size_t count = 300;
+
+ // Client wants to start from some specified time. Chances are
+ // we are better off starting from the end of the time sorted list.
+ LogBufferElementCollection::iterator last;
+ for (last = it = mLogElements.end(); it != mLogElements.begin();
+ /* do nothing */) {
+ --it;
+ LogBufferElement* element = *it;
+ if (element->getRealTime() > start) {
+ last = it;
+ } else if (element->getRealTime() == start) {
+ last = ++it;
+ break;
+ } else if (!--count) {
+ break;
+ }
+ }
+ it = last;
+ }
+
+ log_time curr = start;
+
+ LogBufferElement* lastElement = nullptr; // iterator corruption paranoia
+ static const size_t maxSkip = 4194304; // maximum entries to skip
+ size_t skip = maxSkip;
+ for (; it != mLogElements.end(); ++it) {
+ LogBufferElement* element = *it;
+
+ if (!--skip) {
+ android::prdebug("reader.per: too many elements skipped");
+ break;
+ }
+ if (element == lastElement) {
+ android::prdebug("reader.per: identical elements");
+ break;
+ }
+ lastElement = element;
+
+ if (!privileged && (element->getUid() != uid)) {
+ continue;
+ }
+
+ if (!security && (element->getLogId() == LOG_ID_SECURITY)) {
+ continue;
+ }
+
+ // NB: calling out to another object with wrlock() held (safe)
+ if (filter) {
+ int ret = (*filter)(element, arg);
+ if (ret == false) {
+ continue;
+ }
+ if (ret != true) {
+ break;
+ }
+ }
+
+ bool sameTid = false;
+ if (lastTid) {
+ sameTid = lastTid[element->getLogId()] == element->getTid();
+ // Dropped (chatty) immediately following a valid log from the
+ // same source in the same log buffer indicates we have a
+ // multiple identical squash. chatty that differs source
+ // is due to spam filter. chatty to chatty of different
+ // source is also due to spam filter.
+ lastTid[element->getLogId()] =
+ (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ }
+
+ unlock();
+
+ // range locking in LastLogTimes looks after us
+ curr = element->flushTo(reader, this, sameTid);
+
+ if (curr == element->FLUSH_ERROR) {
+ return curr;
+ }
+
+ skip = maxSkip;
+ rdlock();
+ }
+ unlock();
+
+ return curr;
+}
+
+std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
+ unsigned int logMask) {
+ wrlock();
+
+ std::string ret = stats.format(uid, pid, logMask);
+
+ unlock();
+
+ return ret;
+}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
new file mode 100644
index 000000000..c2d5b9734
--- /dev/null
+++ b/logd/LogBuffer.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_H__
+#define _LOGD_LOG_BUFFER_H__
+
+#include <sys/types.h>
+
+#include <list>
+#include <string>
+
+#include <android/log.h>
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "LogBufferElement.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "LogTimes.h"
+#include "LogWhiteBlackList.h"
+
+//
+// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
+// differentiate without prejudice, we use 1972 to delineate, earlier
+// is likely monotonic, later is real. Otherwise we start using a
+// dividing line between monotonic and realtime if more than a minute
+// difference between them.
+//
+namespace android {
+
+static bool isMonotonic(const log_time& mono) {
+ static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
+ static const uint32_t EPOCH_PLUS_MINUTE = 60;
+
+ if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
+ return false;
+ }
+
+ log_time now(CLOCK_REALTIME);
+
+ /* Timezone and ntp time setup? */
+ if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
+ return true;
+ }
+
+ /* no way to differentiate realtime from monotonic time */
+ if (now.tv_sec < EPOCH_PLUS_MINUTE) {
+ return false;
+ }
+
+ log_time cpu(CLOCK_MONOTONIC);
+ /* too close to call to differentiate monotonic times from realtime */
+ if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
+ return false;
+ }
+
+ /* dividing line half way between monotonic and realtime */
+ return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
+}
+}
+
+typedef std::list<LogBufferElement*> LogBufferElementCollection;
+
+class LogBuffer {
+ LogBufferElementCollection mLogElements;
+ pthread_rwlock_t mLogElementsLock;
+
+ LogStatistics stats;
+
+ PruneList mPrune;
+ // watermark for last per log id
+ LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
+ bool mLastSet[LOG_ID_MAX];
+ // watermark of any worst/chatty uid processing
+ typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
+ LogBufferIteratorMap;
+ LogBufferIteratorMap mLastWorst[LOG_ID_MAX];
+ // watermark of any worst/chatty pid of system processing
+ typedef std::unordered_map<pid_t, LogBufferElementCollection::iterator>
+ LogBufferPidIteratorMap;
+ LogBufferPidIteratorMap mLastWorstPidOfSystem[LOG_ID_MAX];
+
+ unsigned long mMaxSize[LOG_ID_MAX];
+
+ bool monotonic;
+
+ LogTags tags;
+
+ LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+ LogBufferElement* droppedElements[LOG_ID_MAX];
+ void log(LogBufferElement* elem);
+
+ public:
+ LastLogTimes& mTimes;
+
+ explicit LogBuffer(LastLogTimes* times);
+ ~LogBuffer();
+ void init();
+ bool isMonotonic() {
+ return monotonic;
+ }
+
+ int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+ uint16_t len);
+ // lastTid is an optional context to help detect if the last previous
+ // valid message was from the same source so we can differentiate chatty
+ // filter types (identical or expired)
+ log_time flushTo(SocketClient* writer, const log_time& start,
+ pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
+ bool privileged, bool security,
+ int (*filter)(const LogBufferElement* element,
+ void* arg) = nullptr,
+ void* arg = nullptr);
+
+ bool clear(log_id_t id, uid_t uid = AID_ROOT);
+ unsigned long getSize(log_id_t id);
+ int setSize(log_id_t id, unsigned long size);
+ unsigned long getSizeUsed(log_id_t id);
+
+ std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
+
+ void enableStatistics() {
+ stats.enableStatistics();
+ }
+
+ int initPrune(const char* cp) {
+ return mPrune.init(cp);
+ }
+ std::string formatPrune() {
+ return mPrune.format();
+ }
+
+ std::string formatGetEventTag(uid_t uid, const char* name,
+ const char* format) {
+ return tags.formatGetEventTag(uid, name, format);
+ }
+ std::string formatEntry(uint32_t tag, uid_t uid) {
+ return tags.formatEntry(tag, uid);
+ }
+ const char* tagToName(uint32_t tag) {
+ return tags.tagToName(tag);
+ }
+
+ // helper must be protected directly or implicitly by wrlock()/unlock()
+ const char* pidToName(pid_t pid) {
+ return stats.pidToName(pid);
+ }
+ uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
+ const char* uidToName(uid_t uid) {
+ return stats.uidToName(uid);
+ }
+ void wrlock() {
+ pthread_rwlock_wrlock(&mLogElementsLock);
+ }
+ void rdlock() {
+ pthread_rwlock_rdlock(&mLogElementsLock);
+ }
+ void unlock() {
+ pthread_rwlock_unlock(&mLogElementsLock);
+ }
+
+ private:
+ static constexpr size_t minPrune = 4;
+ static constexpr size_t maxPrune = 256;
+ static const log_time pruneMargin;
+
+ void maybePrune(log_id_t id);
+ bool isBusy(log_time watermark);
+ void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
+
+ bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+ LogBufferElementCollection::iterator erase(
+ LogBufferElementCollection::iterator it, bool coalesce = false);
+};
+
+#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
new file mode 100644
index 000000000..ec8193355
--- /dev/null
+++ b/logd/LogBufferElement.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <private/android_logger.h>
+
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogCommand.h"
+#include "LogReader.h"
+#include "LogUtils.h"
+
+const log_time LogBufferElement::FLUSH_ERROR((uint32_t)-1, (uint32_t)-1);
+atomic_int_fast64_t LogBufferElement::sequence(1);
+
+LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
+ uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len)
+ : mUid(uid),
+ mPid(pid),
+ mTid(tid),
+ mRealTime(realtime),
+ mMsgLen(len),
+ mLogId(log_id),
+ mDropped(false) {
+ mMsg = new char[len];
+ memcpy(mMsg, msg, len);
+}
+
+LogBufferElement::LogBufferElement(const LogBufferElement& elem)
+ : mUid(elem.mUid),
+ mPid(elem.mPid),
+ mTid(elem.mTid),
+ mRealTime(elem.mRealTime),
+ mMsgLen(elem.mMsgLen),
+ mLogId(elem.mLogId),
+ mDropped(elem.mDropped) {
+ if (mDropped) {
+ mTag = elem.getTag();
+ } else {
+ mMsg = new char[mMsgLen];
+ memcpy(mMsg, elem.mMsg, mMsgLen);
+ }
+}
+
+LogBufferElement::~LogBufferElement() {
+ if (!mDropped) {
+ delete[] mMsg;
+ }
+}
+
+uint32_t LogBufferElement::getTag() const {
+ // Binary buffers have no tag.
+ if (!isBinary()) {
+ return 0;
+ }
+
+ // Dropped messages store the tag in place of mMsg.
+ if (mDropped) {
+ return mTag;
+ }
+
+ // For non-dropped messages, we get the tag from the message header itself.
+ if (mMsgLen < sizeof(android_event_header_t)) {
+ return 0;
+ }
+
+ return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
+}
+
+uint16_t LogBufferElement::setDropped(uint16_t value) {
+ if (mDropped) {
+ return mDroppedCount = value;
+ }
+
+ // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
+ // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
+ // value in its place.
+ auto old_tag = getTag();
+ delete[] mMsg;
+ mMsg = nullptr;
+
+ mTag = old_tag;
+ mDropped = true;
+ return mDroppedCount = value;
+}
+
+// caller must own and free character string
+char* android::tidToName(pid_t tid) {
+ char* retval = nullptr;
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
+ int fd = open(buffer, O_RDONLY);
+ if (fd >= 0) {
+ ssize_t ret = read(fd, buffer, sizeof(buffer));
+ if (ret >= (ssize_t)sizeof(buffer)) {
+ ret = sizeof(buffer) - 1;
+ }
+ while ((ret > 0) && isspace(buffer[ret - 1])) {
+ --ret;
+ }
+ if (ret > 0) {
+ buffer[ret] = '\0';
+ retval = strdup(buffer);
+ }
+ close(fd);
+ }
+
+ // if nothing for comm, check out cmdline
+ char* name = android::pidToName(tid);
+ if (!retval) {
+ retval = name;
+ name = nullptr;
+ }
+
+ // check if comm is truncated, see if cmdline has full representation
+ if (name) {
+ // impossible for retval to be NULL if name not NULL
+ size_t retval_len = strlen(retval);
+ size_t name_len = strlen(name);
+ // KISS: ToDo: Only checks prefix truncated, not suffix, or both
+ if ((retval_len < name_len) &&
+ !fastcmp<strcmp>(retval, name + name_len - retval_len)) {
+ free(retval);
+ retval = name;
+ } else {
+ free(name);
+ }
+ }
+ return retval;
+}
+
+// assumption: mMsg == NULL
+size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
+ bool lastSame) {
+ static const char tag[] = "chatty";
+
+ if (!__android_log_is_loggable_len(ANDROID_LOG_INFO, tag, strlen(tag),
+ ANDROID_LOG_VERBOSE)) {
+ return 0;
+ }
+
+ static const char format_uid[] = "uid=%u%s%s %s %u line%s";
+ parent->wrlock();
+ const char* name = parent->uidToName(mUid);
+ parent->unlock();
+ const char* commName = android::tidToName(mTid);
+ if (!commName && (mTid != mPid)) {
+ commName = android::tidToName(mPid);
+ }
+ if (!commName) {
+ parent->wrlock();
+ commName = parent->pidToName(mPid);
+ parent->unlock();
+ }
+ if (name && name[0] && commName && (name[0] == commName[0])) {
+ size_t len = strlen(name + 1);
+ if (!strncmp(name + 1, commName + 1, len)) {
+ if (commName[len + 1] == '\0') {
+ free(const_cast<char*>(commName));
+ commName = nullptr;
+ } else {
+ free(const_cast<char*>(name));
+ name = nullptr;
+ }
+ }
+ }
+ if (name) {
+ char* buf = nullptr;
+ asprintf(&buf, "(%s)", name);
+ if (buf) {
+ free(const_cast<char*>(name));
+ name = buf;
+ }
+ }
+ if (commName) {
+ char* buf = nullptr;
+ asprintf(&buf, " %s", commName);
+ if (buf) {
+ free(const_cast<char*>(commName));
+ commName = buf;
+ }
+ }
+ // identical to below to calculate the buffer size required
+ const char* type = lastSame ? "identical" : "expire";
+ size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
+
+ size_t hdrLen;
+ if (isBinary()) {
+ hdrLen = sizeof(android_log_event_string_t);
+ } else {
+ hdrLen = 1 + sizeof(tag);
+ }
+
+ buffer = static_cast<char*>(calloc(1, hdrLen + len + 1));
+ if (!buffer) {
+ free(const_cast<char*>(name));
+ free(const_cast<char*>(commName));
+ return 0;
+ }
+
+ size_t retval = hdrLen + len;
+ if (isBinary()) {
+ android_log_event_string_t* event =
+ reinterpret_cast<android_log_event_string_t*>(buffer);
+
+ event->header.tag = htole32(CHATTY_LOG_TAG);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(len);
+ } else {
+ ++retval;
+ buffer[0] = ANDROID_LOG_INFO;
+ strcpy(buffer + 1, tag);
+ }
+
+ snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
+ free(const_cast<char*>(name));
+ free(const_cast<char*>(commName));
+
+ return retval;
+}
+
+log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+ struct logger_entry entry = {};
+
+ entry.hdr_size = sizeof(struct logger_entry);
+ entry.lid = mLogId;
+ entry.pid = mPid;
+ entry.tid = mTid;
+ entry.uid = mUid;
+ entry.sec = mRealTime.tv_sec;
+ entry.nsec = mRealTime.tv_nsec;
+
+ struct iovec iovec[2];
+ iovec[0].iov_base = &entry;
+ iovec[0].iov_len = entry.hdr_size;
+
+ char* buffer = nullptr;
+
+ if (mDropped) {
+ entry.len = populateDroppedMessage(buffer, parent, lastSame);
+ if (!entry.len) return mRealTime;
+ iovec[1].iov_base = buffer;
+ } else {
+ entry.len = mMsgLen;
+ iovec[1].iov_base = mMsg;
+ }
+ iovec[1].iov_len = entry.len;
+
+ log_time retval = reader->sendDatav(iovec, 1 + (entry.len != 0))
+ ? FLUSH_ERROR
+ : mRealTime;
+
+ if (buffer) free(buffer);
+
+ return retval;
+}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
new file mode 100644
index 000000000..fd790e4be
--- /dev/null
+++ b/logd/LogBufferElement.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdatomic.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+class LogBuffer;
+
+#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve
+ // non-chatty UIDs less than this age in hours
+#define EXPIRE_THRESHOLD 10 // A smaller expire count is considered too
+ // chatty for the temporal expire messages
+#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
+
+class __attribute__((packed)) LogBufferElement {
+ friend LogBuffer;
+
+ // sized to match reality of incoming log packets
+ const uint32_t mUid;
+ const uint32_t mPid;
+ const uint32_t mTid;
+ log_time mRealTime;
+ union {
+ char* mMsg; // mDropped == false
+ int32_t mTag; // mDropped == true
+ };
+ union {
+ const uint16_t mMsgLen; // mDropped == false
+ uint16_t mDroppedCount; // mDropped == true
+ };
+ const uint8_t mLogId;
+ bool mDropped;
+
+ static atomic_int_fast64_t sequence;
+
+ // assumption: mDropped == true
+ size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
+ bool lastSame);
+
+ public:
+ LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, const char* msg, uint16_t len);
+ LogBufferElement(const LogBufferElement& elem);
+ ~LogBufferElement();
+
+ bool isBinary(void) const {
+ return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
+ }
+
+ log_id_t getLogId() const {
+ return static_cast<log_id_t>(mLogId);
+ }
+ uid_t getUid(void) const {
+ return mUid;
+ }
+ pid_t getPid(void) const {
+ return mPid;
+ }
+ pid_t getTid(void) const {
+ return mTid;
+ }
+ uint32_t getTag() const;
+ uint16_t getDropped(void) const {
+ return mDropped ? mDroppedCount : 0;
+ }
+ uint16_t setDropped(uint16_t value);
+ uint16_t getMsgLen() const {
+ return mDropped ? 0 : mMsgLen;
+ }
+ const char* getMsg() const {
+ return mDropped ? nullptr : mMsg;
+ }
+ log_time getRealTime(void) const {
+ return mRealTime;
+ }
+
+ static const log_time FLUSH_ERROR;
+ log_time flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
+};
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
new file mode 100644
index 000000000..8bff9da46
--- /dev/null
+++ b/logd/LogCommand.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "LogCommand.h"
+#include "LogUtils.h"
+
+LogCommand::LogCommand(const char* cmd) : FrameworkCommand(cmd) {
+}
+
+// gets a list of supplementary group IDs associated with
+// the socket peer. This is implemented by opening
+// /proc/PID/status and look for the "Group:" line.
+//
+// This function introduces races especially since status
+// can change 'shape' while reading, the net result is err
+// on lack of permission.
+//
+// Race-free alternative is to introduce pairs of sockets
+// and threads for each command and reading, one each that
+// has open permissions, and one that has restricted
+// permissions.
+
+static bool groupIsLog(char* buf) {
+ char* ptr;
+ static const char ws[] = " \n";
+
+ for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(nullptr, ws, &ptr)) {
+ errno = 0;
+ gid_t Gid = strtol(buf, nullptr, 10);
+ if (errno != 0) {
+ return false;
+ }
+ if (Gid == AID_LOG) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid) {
+ if ((uid == AID_ROOT) || (uid == AID_SYSTEM) || (uid == AID_LOG)) {
+ return true;
+ }
+
+ if ((gid == AID_ROOT) || (gid == AID_SYSTEM) || (gid == AID_LOG)) {
+ return true;
+ }
+
+ // FYI We will typically be here for 'adb logcat'
+ char filename[256];
+ snprintf(filename, sizeof(filename), "/proc/%u/status", pid);
+
+ bool ret;
+ bool foundLog = false;
+ bool foundGid = false;
+ bool foundUid = false;
+
+ //
+ // Reading /proc/<pid>/status is rife with race conditions. All of /proc
+ // suffers from this and its use should be minimized. However, we have no
+ // choice.
+ //
+ // Notably the content from one 4KB page to the next 4KB page can be from a
+ // change in shape even if we are gracious enough to attempt to read
+ // atomically. getline can not even guarantee a page read is not split up
+ // and in effect can read from different vintages of the content.
+ //
+ // We are finding out in the field that a 'logcat -c' via adb occasionally
+ // is returned with permission denied when we did only one pass and thus
+ // breaking scripts. For security we still err on denying access if in
+ // doubt, but we expect the falses should be reduced significantly as
+ // three times is a charm.
+ //
+ for (int retry = 3; !(ret = foundGid && foundUid && foundLog) && retry;
+ --retry) {
+ FILE* file = fopen(filename, "r");
+ if (!file) {
+ continue;
+ }
+
+ char* line = nullptr;
+ size_t len = 0;
+ while (getline(&line, &len, file) > 0) {
+ static const char groups_string[] = "Groups:\t";
+ static const char uid_string[] = "Uid:\t";
+ static const char gid_string[] = "Gid:\t";
+
+ if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
+ if (groupIsLog(line + sizeof(groups_string) - 1)) {
+ foundLog = true;
+ }
+ } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
+ uid_t u[4] = { (uid_t)-1, (uid_t)-1, (uid_t)-1, (uid_t)-1 };
+
+ sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u", &u[0],
+ &u[1], &u[2], &u[3]);
+
+ // Protect against PID reuse by checking that UID is the same
+ if ((uid == u[0]) && (uid == u[1]) && (uid == u[2]) &&
+ (uid == u[3])) {
+ foundUid = true;
+ }
+ } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
+ gid_t g[4] = { (gid_t)-1, (gid_t)-1, (gid_t)-1, (gid_t)-1 };
+
+ sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u", &g[0],
+ &g[1], &g[2], &g[3]);
+
+ // Protect against PID reuse by checking that GID is the same
+ if ((gid == g[0]) && (gid == g[1]) && (gid == g[2]) &&
+ (gid == g[3])) {
+ foundGid = true;
+ }
+ }
+ }
+ free(line);
+ fclose(file);
+ }
+
+ return ret;
+}
+
+bool clientHasLogCredentials(SocketClient* cli) {
+ return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
+}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
new file mode 100644
index 000000000..e10ffa0e6
--- /dev/null
+++ b/logd/LogCommand.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_COMMAND_H
+#define _LOGD_COMMAND_H
+
+#include <sysutils/FrameworkCommand.h>
+#include <sysutils/SocketClient.h>
+
+class LogCommand : public FrameworkCommand {
+ public:
+ explicit LogCommand(const char* cmd);
+ virtual ~LogCommand() {
+ }
+};
+
+#endif
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
new file mode 100644
index 000000000..edd326aec
--- /dev/null
+++ b/logd/LogKlog.cpp
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+#include <syslog.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogBuffer.h"
+#include "LogKlog.h"
+#include "LogReader.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', '0' + (LOG_SYSLOG | (PRI)) / 10, '0' + (LOG_SYSLOG | (PRI)) % 10, '>'
+
+static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
+
+// List of the _only_ needles we supply here to android::strnstr
+static const char suspendStr[] = "PM: suspend entry ";
+static const char resumeStr[] = "PM: suspend exit ";
+static const char suspendedStr[] = "Suspended for ";
+static const char healthdStr[] = "healthd";
+static const char batteryStr[] = ": battery ";
+static const char auditStr[] = " audit(";
+static const char klogdStr[] = "logd.klogd: ";
+
+// Parsing is hard
+
+// called if we see a '<', s is the next character, returns pointer after '>'
+static char* is_prio(char* s, ssize_t len) {
+ if ((len <= 0) || !isdigit(*s++)) return nullptr;
+ --len;
+ static const size_t max_prio_len = (len < 4) ? len : 4;
+ size_t priolen = 0;
+ char c;
+ while (((c = *s++)) && (++priolen <= max_prio_len)) {
+ if (!isdigit(c)) return ((c == '>') && (*s == '[')) ? s : nullptr;
+ }
+ return nullptr;
+}
+
+// called if we see a '[', s is the next character, returns pointer after ']'
+static char* is_timestamp(char* s, ssize_t len) {
+ while ((len > 0) && (*s == ' ')) {
+ ++s;
+ --len;
+ }
+ if ((len <= 0) || !isdigit(*s++)) return nullptr;
+ --len;
+ bool first_period = true;
+ char c;
+ while ((len > 0) && ((c = *s++))) {
+ --len;
+ if ((c == '.') && first_period) {
+ first_period = false;
+ } else if (!isdigit(c)) {
+ return ((c == ']') && !first_period && (*s == ' ')) ? s : nullptr;
+ }
+ }
+ return nullptr;
+}
+
+// Like strtok_r with "\r\n" except that we look for log signatures (regex)
+// \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[]
+// *[0-9]+[.][0-9]+[]] \)
+// and split if we see a second one without a newline.
+// We allow nuls in content, monitoring the overall length and sub-length of
+// the discovered tokens.
+
+#define SIGNATURE_MASK 0xF0
+// <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature
+#define LESS_THAN_SIG SIGNATURE_MASK
+#define OPEN_BRACKET_SIG ((SIGNATURE_MASK << 1) & SIGNATURE_MASK)
+// space is one more than <digit> of 9
+#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
+
+char* android::log_strntok_r(char* s, ssize_t& len, char*& last,
+ ssize_t& sublen) {
+ sublen = 0;
+ if (len <= 0) return nullptr;
+ if (!s) {
+ if (!(s = last)) return nullptr;
+ // fixup for log signature split <,
+ // LESS_THAN_SIG + <digit>
+ if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
+ *s = (*s & ~SIGNATURE_MASK) + '0';
+ *--s = '<';
+ ++len;
+ }
+ // fixup for log signature split [,
+ // OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
+ if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
+ *s = (*s == OPEN_BRACKET_SPACE) ? ' ' : (*s & ~SIGNATURE_MASK) + '0';
+ *--s = '[';
+ ++len;
+ }
+ }
+
+ while ((len > 0) && ((*s == '\r') || (*s == '\n'))) {
+ ++s;
+ --len;
+ }
+
+ if (len <= 0) return last = nullptr;
+ char *peek, *tok = s;
+
+ for (;;) {
+ if (len <= 0) {
+ last = nullptr;
+ return tok;
+ }
+ char c = *s++;
+ --len;
+ ssize_t adjust;
+ switch (c) {
+ case '\r':
+ case '\n':
+ s[-1] = '\0';
+ last = s;
+ return tok;
+
+ case '<':
+ peek = is_prio(s, len);
+ if (!peek) break;
+ if (s != (tok + 1)) { // not first?
+ s[-1] = '\0';
+ *s &= ~SIGNATURE_MASK;
+ *s |= LESS_THAN_SIG; // signature for '<'
+ last = s;
+ return tok;
+ }
+ adjust = peek - s;
+ if (adjust > len) {
+ adjust = len;
+ }
+ sublen += adjust;
+ len -= adjust;
+ s = peek;
+ if ((*s == '[') && ((peek = is_timestamp(s + 1, len - 1)))) {
+ adjust = peek - s;
+ if (adjust > len) {
+ adjust = len;
+ }
+ sublen += adjust;
+ len -= adjust;
+ s = peek;
+ }
+ break;
+
+ case '[':
+ peek = is_timestamp(s, len);
+ if (!peek) break;
+ if (s != (tok + 1)) { // not first?
+ s[-1] = '\0';
+ if (*s == ' ') {
+ *s = OPEN_BRACKET_SPACE;
+ } else {
+ *s &= ~SIGNATURE_MASK;
+ *s |= OPEN_BRACKET_SIG; // signature for '['
+ }
+ last = s;
+ return tok;
+ }
+ adjust = peek - s;
+ if (adjust > len) {
+ adjust = len;
+ }
+ sublen += adjust;
+ len -= adjust;
+ s = peek;
+ break;
+ }
+ ++sublen;
+ }
+ // NOTREACHED
+}
+
+log_time LogKlog::correction = (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+ ? log_time(log_time::EPOCH)
+ : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
+
+LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
+ bool auditd)
+ : SocketListener(fdRead, false),
+ logbuf(buf),
+ reader(reader),
+ signature(CLOCK_MONOTONIC),
+ initialized(false),
+ enableLogging(true),
+ auditd(auditd) {
+ static const char klogd_message[] = "%s%s%" PRIu64 "\n";
+ char buffer[strlen(priority_message) + strlen(klogdStr) +
+ strlen(klogd_message) + 20];
+ snprintf(buffer, sizeof(buffer), klogd_message, priority_message, klogdStr,
+ signature.nsec());
+ write(fdWrite, buffer, strlen(buffer));
+}
+
+bool LogKlog::onDataAvailable(SocketClient* cli) {
+ if (!initialized) {
+ prctl(PR_SET_NAME, "logd.klogd");
+ initialized = true;
+ enableLogging = false;
+ }
+
+ char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
+ ssize_t len = 0;
+
+ for (;;) {
+ ssize_t retval = 0;
+ if (len < (ssize_t)(sizeof(buffer) - 1)) {
+ retval =
+ read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
+ }
+ if ((retval == 0) && (len <= 0)) {
+ break;
+ }
+ if (retval < 0) {
+ return false;
+ }
+ len += retval;
+ bool full = len == (sizeof(buffer) - 1);
+ char* ep = buffer + len;
+ *ep = '\0';
+ ssize_t sublen;
+ for (char *ptr = nullptr, *tok = buffer;
+ !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
+ tok = nullptr) {
+ if (((tok + sublen) >= ep) && (retval != 0) && full) {
+ if (sublen > 0) memmove(buffer, tok, sublen);
+ len = sublen;
+ break;
+ }
+ if ((sublen > 0) && *tok) {
+ log(tok, sublen);
+ }
+ }
+ }
+
+ return true;
+}
+
+void LogKlog::calculateCorrection(const log_time& monotonic,
+ const char* real_string, ssize_t len) {
+ static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
+ if (len < (ssize_t)(strlen(real_format) + 5)) return;
+
+ log_time real(log_time::EPOCH);
+ const char* ep = real.strptime(real_string, real_format);
+ if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
+ return;
+ }
+ // kernel report UTC, log_time::strptime is localtime from calendar.
+ // Bionic and liblog strptime does not support %z or %Z to pick up
+ // timezone so we are calculating our own correction.
+ time_t now = real.tv_sec;
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_isdst = -1;
+ localtime_r(&now, &tm);
+ if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
+ real = log_time(log_time::EPOCH);
+ } else {
+ real.tv_sec += tm.tm_gmtoff;
+ }
+ if (monotonic > real) {
+ correction = log_time(log_time::EPOCH);
+ } else {
+ correction = real - monotonic;
+ }
+}
+
+log_time LogKlog::sniffTime(const char*& buf, ssize_t len, bool reverse) {
+ log_time now(log_time::EPOCH);
+ if (len <= 0) return now;
+
+ const char* cp = nullptr;
+ if ((len > 10) && (*buf == '[')) {
+ cp = now.strptime(buf, "[ %s.%q]"); // can index beyond buffer bounds
+ if (cp && (cp > &buf[len - 1])) cp = nullptr;
+ }
+ if (cp) {
+ len -= cp - buf;
+ if ((len > 0) && isspace(*cp)) {
+ ++cp;
+ --len;
+ }
+ buf = cp;
+
+ if (isMonotonic()) return now;
+
+ const char* b;
+ if (((b = android::strnstr(cp, len, suspendStr))) &&
+ (((b += strlen(suspendStr)) - cp) < len)) {
+ len -= b - cp;
+ calculateCorrection(now, b, len);
+ } else if (((b = android::strnstr(cp, len, resumeStr))) &&
+ (((b += strlen(resumeStr)) - cp) < len)) {
+ len -= b - cp;
+ calculateCorrection(now, b, len);
+ } else if (((b = android::strnstr(cp, len, healthdStr))) &&
+ (((b += strlen(healthdStr)) - cp) < len) &&
+ ((b = android::strnstr(b, len -= b - cp, batteryStr))) &&
+ (((b += strlen(batteryStr)) - cp) < len)) {
+ // NB: healthd is roughly 150us late, so we use it instead to
+ // trigger a check for ntp-induced or hardware clock drift.
+ log_time real(CLOCK_REALTIME);
+ log_time mono(CLOCK_MONOTONIC);
+ correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
+ } else if (((b = android::strnstr(cp, len, suspendedStr))) &&
+ (((b += strlen(suspendStr)) - cp) < len)) {
+ len -= b - cp;
+ log_time real(log_time::EPOCH);
+ char* endp;
+ real.tv_sec = strtol(b, &endp, 10);
+ if ((*endp == '.') && ((endp - b) < len)) {
+ unsigned long multiplier = NS_PER_SEC;
+ real.tv_nsec = 0;
+ len -= endp - b;
+ while (--len && isdigit(*++endp) && (multiplier /= 10)) {
+ real.tv_nsec += (*endp - '0') * multiplier;
+ }
+ if (reverse) {
+ if (real > correction) {
+ correction = log_time(log_time::EPOCH);
+ } else {
+ correction -= real;
+ }
+ } else {
+ correction += real;
+ }
+ }
+ }
+
+ convertMonotonicToReal(now);
+ } else {
+ if (isMonotonic()) {
+ now = log_time(CLOCK_MONOTONIC);
+ } else {
+ now = log_time(CLOCK_REALTIME);
+ }
+ }
+ return now;
+}
+
+pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
+ if (len <= 0) return 0;
+
+ const char* cp = buf;
+ // sscanf does a strlen, let's check if the string is not nul terminated.
+ // pseudo out-of-bounds access since we always have an extra char on buffer.
+ if (((ssize_t)strnlen(cp, len) == len) && cp[len]) {
+ return 0;
+ }
+ // HTC kernels with modified printk "c0 1648 "
+ if ((len > 9) && (cp[0] == 'c') && isdigit(cp[1]) &&
+ (isdigit(cp[2]) || (cp[2] == ' ')) && (cp[3] == ' ')) {
+ bool gotDigit = false;
+ int i;
+ for (i = 4; i < 9; ++i) {
+ if (isdigit(cp[i])) {
+ gotDigit = true;
+ } else if (gotDigit || (cp[i] != ' ')) {
+ break;
+ }
+ }
+ if ((i == 9) && (cp[i] == ' ')) {
+ int pid = 0;
+ char dummy;
+ if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
+ buf = cp + 10; // skip-it-all
+ return pid;
+ }
+ }
+ }
+ while (len) {
+ // Mediatek kernels with modified printk
+ if (*cp == '[') {
+ int pid = 0;
+ char dummy;
+ if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+ return pid;
+ }
+ break; // Only the first one
+ }
+ ++cp;
+ --len;
+ }
+ return 0;
+}
+
+// kernel log prefix, convert to a kernel log priority number
+static int parseKernelPrio(const char*& buf, ssize_t len) {
+ int pri = LOG_USER | LOG_INFO;
+ const char* cp = buf;
+ if ((len > 0) && (*cp == '<')) {
+ pri = 0;
+ while (--len && isdigit(*++cp)) {
+ pri = (pri * 10) + *cp - '0';
+ }
+ if ((len > 0) && (*cp == '>')) {
+ ++cp;
+ } else {
+ cp = buf;
+ pri = LOG_USER | LOG_INFO;
+ }
+ buf = cp;
+ }
+ return pri;
+}
+
+// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
+// compensated start time.
+void LogKlog::synchronize(const char* buf, ssize_t len) {
+ const char* cp = android::strnstr(buf, len, suspendStr);
+ if (!cp) {
+ cp = android::strnstr(buf, len, resumeStr);
+ if (!cp) return;
+ } else {
+ const char* rp = android::strnstr(buf, len, resumeStr);
+ if (rp && (rp < cp)) cp = rp;
+ }
+
+ do {
+ --cp;
+ } while ((cp > buf) && (*cp != '\n'));
+ if (*cp == '\n') {
+ ++cp;
+ }
+ parseKernelPrio(cp, len - (cp - buf));
+
+ log_time now = sniffTime(cp, len - (cp - buf), true);
+
+ const char* suspended = android::strnstr(buf, len, suspendedStr);
+ if (!suspended || (suspended > cp)) {
+ return;
+ }
+ cp = suspended;
+
+ do {
+ --cp;
+ } while ((cp > buf) && (*cp != '\n'));
+ if (*cp == '\n') {
+ ++cp;
+ }
+ parseKernelPrio(cp, len - (cp - buf));
+
+ sniffTime(cp, len - (cp - buf), true);
+}
+
+// Convert kernel log priority number into an Android Logger priority number
+static int convertKernelPrioToAndroidPrio(int pri) {
+ switch (pri & LOG_PRIMASK) {
+ case LOG_EMERG:
+ case LOG_ALERT:
+ case LOG_CRIT:
+ return ANDROID_LOG_FATAL;
+
+ case LOG_ERR:
+ return ANDROID_LOG_ERROR;
+
+ case LOG_WARNING:
+ return ANDROID_LOG_WARN;
+
+ default:
+ case LOG_NOTICE:
+ case LOG_INFO:
+ break;
+
+ case LOG_DEBUG:
+ return ANDROID_LOG_DEBUG;
+ }
+
+ return ANDROID_LOG_INFO;
+}
+
+static const char* strnrchr(const char* s, ssize_t len, char c) {
+ const char* save = nullptr;
+ for (; len > 0; ++s, len--) {
+ if (*s == c) {
+ save = s;
+ }
+ }
+ return save;
+}
+
+//
+// log a message into the kernel log buffer
+//
+// Filter rules to parse <PRI> <TIME> <tag> and <message> in order for
+// them to appear correct in the logcat output:
+//
+// LOG_KERN (0):
+// <PRI>[<TIME>] <tag> ":" <message>
+// <PRI>[<TIME>] <tag> <tag> ":" <message>
+// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
+// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
+// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
+// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
+// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
+// <PRI>[<TIME>] "[INFO]"<tag> : <message>
+// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
+// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
+// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
+// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
+// <PRI+TAG>[<TIME>] (see sys/syslog.h)
+// Observe:
+// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
+// Maximum tag words = 2
+// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
+// Not a Tag if there is no message content.
+// leading additional spaces means no tag, inherit last tag.
+// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
+// Drop:
+// empty messages
+// messages with ' audit(' in them if auditd is running
+// logd.klogd:
+// return -1 if message logd.klogd: <signature>
+//
+int LogKlog::log(const char* buf, ssize_t len) {
+ if (auditd && android::strnstr(buf, len, auditStr)) {
+ return 0;
+ }
+
+ const char* p = buf;
+ int pri = parseKernelPrio(p, len);
+
+ log_time now = sniffTime(p, len - (p - buf), false);
+
+ // sniff for start marker
+ const char* start = android::strnstr(p, len - (p - buf), klogdStr);
+ if (start) {
+ uint64_t sig = strtoll(start + strlen(klogdStr), nullptr, 10);
+ if (sig == signature.nsec()) {
+ if (initialized) {
+ enableLogging = true;
+ } else {
+ enableLogging = false;
+ }
+ return -1;
+ }
+ return 0;
+ }
+
+ if (!enableLogging) {
+ return 0;
+ }
+
+ // Parse pid, tid and uid
+ const pid_t pid = sniffPid(p, len - (p - buf));
+ const pid_t tid = pid;
+ uid_t uid = AID_ROOT;
+ if (pid) {
+ logbuf->wrlock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ }
+
+ // Parse (rules at top) to pull out a tag from the incoming kernel message.
+ // Some may view the following as an ugly heuristic, the desire is to
+ // beautify the kernel logs into an Android Logging format; the goal is
+ // admirable but costly.
+ while ((p < &buf[len]) && (isspace(*p) || !*p)) {
+ ++p;
+ }
+ if (p >= &buf[len]) { // timestamp, no content
+ return 0;
+ }
+ start = p;
+ const char* tag = "";
+ const char* etag = tag;
+ ssize_t taglen = len - (p - buf);
+ const char* bt = p;
+
+ static const char infoBrace[] = "[INFO]";
+ static const ssize_t infoBraceLen = strlen(infoBrace);
+ if ((taglen >= infoBraceLen) &&
+ !fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = p + infoBraceLen;
+ taglen -= infoBraceLen;
+ }
+
+ const char* et;
+ for (et = bt; (taglen > 0) && *et && (*et != ':') && !isspace(*et);
+ ++et, --taglen) {
+ // skip ':' within [ ... ]
+ if (*et == '[') {
+ while ((taglen > 0) && *et && *et != ']') {
+ ++et;
+ --taglen;
+ }
+ if (taglen <= 0) {
+ break;
+ }
+ }
+ }
+ const char* cp;
+ for (cp = et; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
+ }
+
+ // Validate tag
+ ssize_t size = et - bt;
+ if ((taglen > 0) && (size > 0)) {
+ if (*cp == ':') {
+ // ToDo: handle case insensitive colon separated logging stutter:
+ // <tag> : <tag>: ...
+
+ // One Word
+ tag = bt;
+ etag = et;
+ p = cp + 1;
+ } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
+ // clean up any tag stutter
+ if (!fastcmp<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+ // <PRI>[<TIME>] <tag> <tag> : message
+ // <PRI>[<TIME>] <tag> <tag>: message
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><stuff>' : message
+ const char* b = cp;
+ cp += size;
+ taglen -= size;
+ while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
+ }
+ const char* e;
+ for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
+ }
+ if ((taglen > 0) && (*cp == ':')) {
+ tag = b;
+ etag = e;
+ p = cp + 1;
+ }
+ } else {
+ // what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
+ static const char host[] = "_host";
+ static const ssize_t hostlen = strlen(host);
+ if ((size > hostlen) &&
+ !fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
+ !fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
+ const char* b = cp;
+ cp += size - hostlen;
+ taglen -= size - hostlen;
+ if (*cp == '.') {
+ while ((--taglen > 0) && !isspace(*++cp) &&
+ (*cp != ':')) {
+ }
+ const char* e;
+ for (e = cp; (taglen > 0) && isspace(*cp);
+ ++cp, --taglen) {
+ }
+ if ((taglen > 0) && (*cp == ':')) {
+ tag = b;
+ etag = e;
+ p = cp + 1;
+ }
+ }
+ } else {
+ goto twoWord;
+ }
+ }
+ } else {
+ // <PRI>[<TIME>] <tag> <stuff>' : message
+ twoWord:
+ while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
+ }
+ const char* e;
+ for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
+ }
+ // Two words
+ if ((taglen > 0) && (*cp == ':')) {
+ tag = bt;
+ etag = e;
+ p = cp + 1;
+ }
+ }
+ } // else no tag
+
+ static const char cpu[] = "CPU";
+ static const ssize_t cpuLen = strlen(cpu);
+ static const char warning[] = "WARNING";
+ static const ssize_t warningLen = strlen(warning);
+ static const char error[] = "ERROR";
+ static const ssize_t errorLen = strlen(error);
+ static const char info[] = "INFO";
+ static const ssize_t infoLen = strlen(info);
+
+ size = etag - tag;
+ if ((size <= 1) ||
+ // register names like x9
+ ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) ||
+ // register names like x18 but not driver names like en0
+ ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) ||
+ // blacklist
+ ((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen)) ||
+ ((size == warningLen) &&
+ !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
+ ((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen)) ||
+ ((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
+ p = start;
+ etag = tag = "";
+ }
+
+ // Suppress additional stutter in tag:
+ // eg: [143:healthd]healthd -> [143:healthd]
+ taglen = etag - tag;
+ // Mediatek-special printk induced stutter
+ const char* mp = strnrchr(tag, taglen, ']');
+ if (mp && (++mp < etag)) {
+ ssize_t s = etag - mp;
+ if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
+ taglen = mp - tag;
+ }
+ }
+ // Deal with sloppy and simplistic harmless p = cp + 1 etc above.
+ if (len < (p - buf)) {
+ p = &buf[len];
+ }
+ // skip leading space
+ while ((p < &buf[len]) && (isspace(*p) || !*p)) {
+ ++p;
+ }
+ // truncate trailing space or nuls
+ ssize_t b = len - (p - buf);
+ while ((b > 0) && (isspace(p[b - 1]) || !p[b - 1])) {
+ --b;
+ }
+ // trick ... allow tag with empty content to be logged. log() drops empty
+ if ((b <= 0) && (taglen > 0)) {
+ p = " ";
+ b = 1;
+ }
+ // paranoid sanity check, can not happen ...
+ if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
+ b = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ if (taglen > LOGGER_ENTRY_MAX_PAYLOAD) {
+ taglen = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ // calculate buffer copy requirements
+ ssize_t n = 1 + taglen + 1 + b + 1;
+ // paranoid sanity check, first two just can not happen ...
+ if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
+ return -EINVAL;
+ }
+
+ // Careful.
+ // We are using the stack to house the log buffer for speed reasons.
+ // If we malloc'd this buffer, we could get away without n's USHRT_MAX
+ // test above, but we would then required a max(n, USHRT_MAX) as
+ // truncating length argument to logbuf->log() below. Gain is protection
+ // of stack sanity and speedup, loss is truncated long-line content.
+ char newstr[n];
+ char* np = newstr;
+
+ // Convert priority into single-byte Android logger priority
+ *np = convertKernelPrioToAndroidPrio(pri);
+ ++np;
+
+ // Copy parsed tag following priority
+ memcpy(np, tag, taglen);
+ np += taglen;
+ *np = '\0';
+ ++np;
+
+ // Copy main message to the remainder
+ memcpy(np, p, b);
+ np[b] = '\0';
+
+ if (!isMonotonic()) {
+ // Watch out for singular race conditions with timezone causing near
+ // integer quarter-hour jumps in the time and compensate accordingly.
+ // Entries will be temporal within near_seconds * 2. b/21868540
+ static uint32_t vote_time[3];
+ vote_time[2] = vote_time[1];
+ vote_time[1] = vote_time[0];
+ vote_time[0] = now.tv_sec;
+
+ if (vote_time[1] && vote_time[2]) {
+ static const unsigned near_seconds = 10;
+ static const unsigned timezones_seconds = 900;
+ int diff0 = (vote_time[0] - vote_time[1]) / near_seconds;
+ unsigned abs0 = (diff0 < 0) ? -diff0 : diff0;
+ int diff1 = (vote_time[1] - vote_time[2]) / near_seconds;
+ unsigned abs1 = (diff1 < 0) ? -diff1 : diff1;
+ if ((abs1 <= 1) && // last two were in agreement on timezone
+ ((abs0 + 1) % (timezones_seconds / near_seconds)) <= 2) {
+ abs0 = (abs0 + 1) / (timezones_seconds / near_seconds) *
+ timezones_seconds;
+ now.tv_sec -= (diff0 < 0) ? -abs0 : abs0;
+ }
+ }
+ }
+
+ // Log message
+ int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (uint16_t)n);
+
+ // notify readers
+ if (rc > 0) {
+ reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
+ }
+
+ return rc;
+}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
new file mode 100644
index 000000000..6bfd6a841
--- /dev/null
+++ b/logd/LogKlog.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_KLOG_H__
+#define _LOGD_LOG_KLOG_H__
+
+#include <private/android_logger.h>
+#include <sysutils/SocketListener.h>
+
+class LogBuffer;
+class LogReader;
+
+class LogKlog : public SocketListener {
+ LogBuffer* logbuf;
+ LogReader* reader;
+ const log_time signature;
+ // Set once thread is started, separates KLOG_ACTION_READ_ALL
+ // and KLOG_ACTION_READ phases.
+ bool initialized;
+ // Used during each of the above phases to control logging.
+ bool enableLogging;
+ // set if we are also running auditd, to filter out audit reports from
+ // our copy of the kernel log
+ bool auditd;
+
+ static log_time correction;
+
+ public:
+ LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
+ bool auditd);
+ int log(const char* buf, ssize_t len);
+ void synchronize(const char* buf, ssize_t len);
+
+ bool isMonotonic() {
+ return logbuf->isMonotonic();
+ }
+ static void convertMonotonicToReal(log_time& real) {
+ real += correction;
+ }
+ static void convertRealToMonotonic(log_time& real) {
+ real -= correction;
+ }
+
+ protected:
+ log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+ pid_t sniffPid(const char*& buf, ssize_t len);
+ void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+ virtual bool onDataAvailable(SocketClient* cli);
+};
+
+#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
new file mode 100644
index 000000000..ba610424f
--- /dev/null
+++ b/logd/LogListener.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012-2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+#include <sys/cdefs.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogBuffer.h"
+#include "LogListener.h"
+#include "LogUtils.h"
+
+LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+ : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
+
+bool LogListener::onDataAvailable(SocketClient* cli) {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.writer");
+ name_set = true;
+ }
+
+ // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
+ char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
+ struct iovec iov = { buffer, sizeof(buffer) - 1 };
+
+ alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
+ struct msghdr hdr = {
+ nullptr, 0, &iov, 1, control, sizeof(control), 0,
+ };
+
+ int socket = cli->getSocket();
+
+ // To clear the entire buffer is secure/safe, but this contributes to 1.68%
+ // overhead under logging load. We are safe because we check counts, but
+ // still need to clear null terminator
+ // memset(buffer, 0, sizeof(buffer));
+ ssize_t n = recvmsg(socket, &hdr, 0);
+ if (n <= (ssize_t)(sizeof(android_log_header_t))) {
+ return false;
+ }
+
+ buffer[n] = 0;
+
+ struct ucred* cred = nullptr;
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ while (cmsg != nullptr) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS) {
+ cred = (struct ucred*)CMSG_DATA(cmsg);
+ break;
+ }
+ cmsg = CMSG_NXTHDR(&hdr, cmsg);
+ }
+
+ if (cred == nullptr) {
+ return false;
+ }
+
+ if (cred->uid == AID_LOGD) {
+ // ignore log messages we send to ourself.
+ // Such log messages are often generated by libraries we depend on
+ // which use standard Android logging.
+ return false;
+ }
+
+ android_log_header_t* header =
+ reinterpret_cast<android_log_header_t*>(buffer);
+ log_id_t logId = static_cast<log_id_t>(header->id);
+ if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||
+ logId == LOG_ID_KERNEL) {
+ return false;
+ }
+
+ if ((logId == LOG_ID_SECURITY) &&
+ (!__android_log_security() ||
+ !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
+ return false;
+ }
+
+ char* msg = ((char*)buffer) + sizeof(android_log_header_t);
+ n -= sizeof(android_log_header_t);
+
+ // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
+ // truncated message to the logs.
+
+ int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+ ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
+ if (res > 0) {
+ reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
+ }
+
+ return true;
+}
+
+int LogListener::getLogSocket() {
+ static const char socketName[] = "logdw";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) { // logd started up in init.sh
+ sock = socket_local_server(
+ socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
+
+ int on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+ return -1;
+ }
+ }
+ return sock;
+}
diff --git a/logd/LogListener.h b/logd/LogListener.h
new file mode 100644
index 000000000..8fe3da46d
--- /dev/null
+++ b/logd/LogListener.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_LISTENER_H__
+#define _LOGD_LOG_LISTENER_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogReader.h"
+
+class LogListener : public SocketListener {
+ LogBuffer* logbuf;
+ LogReader* reader;
+
+ public:
+ LogListener(LogBuffer* buf, LogReader* reader);
+
+ protected:
+ virtual bool onDataAvailable(SocketClient* cli);
+
+ private:
+ static int getLogSocket();
+};
+
+#endif
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
new file mode 100644
index 000000000..9db8c0047
--- /dev/null
+++ b/logd/LogReader.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <cutils/sockets.h>
+#include <private/android_logger.h>
+
+#include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReader.h"
+#include "LogUtils.h"
+
+LogReader::LogReader(LogBuffer* logbuf)
+ : SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
+}
+
+// When we are notified a new log entry is available, inform
+// listening sockets who are watching this entry's log id.
+void LogReader::notifyNewLog(log_mask_t logMask) {
+ FlushCommand command(*this, logMask);
+ runOnEachSocket(&command);
+}
+
+// Note returning false will release the SocketClient instance.
+bool LogReader::onDataAvailable(SocketClient* cli) {
+ static bool name_set;
+ if (!name_set) {
+ prctl(PR_SET_NAME, "logd.reader");
+ name_set = true;
+ }
+
+ char buffer[255];
+
+ int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
+ if (len <= 0) {
+ doSocketDelete(cli);
+ return false;
+ }
+ buffer[len] = '\0';
+
+ // Clients are only allowed to send one command, disconnect them if they
+ // send another.
+ LogTimeEntry::wrlock();
+ for (const auto& entry : mLogbuf.mTimes) {
+ if (entry->mClient == cli) {
+ entry->release_Locked();
+ LogTimeEntry::unlock();
+ return false;
+ }
+ }
+ LogTimeEntry::unlock();
+
+ unsigned long tail = 0;
+ static const char _tail[] = " tail=";
+ char* cp = strstr(buffer, _tail);
+ if (cp) {
+ tail = atol(cp + sizeof(_tail) - 1);
+ }
+
+ log_time start(log_time::EPOCH);
+ static const char _start[] = " start=";
+ cp = strstr(buffer, _start);
+ if (cp) {
+ // Parse errors will result in current time
+ start.strptime(cp + sizeof(_start) - 1, "%s.%q");
+ }
+
+ uint64_t timeout = 0;
+ static const char _timeout[] = " timeout=";
+ cp = strstr(buffer, _timeout);
+ if (cp) {
+ timeout = atol(cp + sizeof(_timeout) - 1) * NS_PER_SEC +
+ log_time(CLOCK_REALTIME).nsec();
+ }
+
+ unsigned int logMask = -1;
+ static const char _logIds[] = " lids=";
+ cp = strstr(buffer, _logIds);
+ if (cp) {
+ logMask = 0;
+ cp += sizeof(_logIds) - 1;
+ while (*cp && *cp != '\0') {
+ int val = 0;
+ while (isdigit(*cp)) {
+ val = val * 10 + *cp - '0';
+ ++cp;
+ }
+ logMask |= 1 << val;
+ if (*cp != ',') {
+ break;
+ }
+ ++cp;
+ }
+ }
+
+ pid_t pid = 0;
+ static const char _pid[] = " pid=";
+ cp = strstr(buffer, _pid);
+ if (cp) {
+ pid = atol(cp + sizeof(_pid) - 1);
+ }
+
+ bool nonBlock = false;
+ if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
+ // Allow writer to get some cycles, and wait for pending notifications
+ sched_yield();
+ LogTimeEntry::wrlock();
+ LogTimeEntry::unlock();
+ sched_yield();
+ nonBlock = true;
+ }
+
+ log_time sequence = start;
+ //
+ // This somewhat expensive data validation operation is required
+ // for non-blocking, with timeout. The incoming timestamp must be
+ // in range of the list, if not, return immediately. This is
+ // used to prevent us from from getting stuck in timeout processing
+ // with an invalid time.
+ //
+ // Find if time is really present in the logs, monotonic or real, implicit
+ // conversion from monotonic or real as necessary to perform the check.
+ // Exit in the check loop ASAP as you find a transition from older to
+ // newer, but use the last entry found to ensure overlap.
+ //
+ if (nonBlock && (sequence != log_time::EPOCH) && timeout) {
+ class LogFindStart { // A lambda by another name
+ private:
+ const pid_t mPid;
+ const unsigned mLogMask;
+ bool mStartTimeSet;
+ log_time mStart;
+ log_time& mSequence;
+ log_time mLast;
+ bool mIsMonotonic;
+
+ public:
+ LogFindStart(pid_t pid, unsigned logMask, log_time& sequence,
+ bool isMonotonic)
+ : mPid(pid),
+ mLogMask(logMask),
+ mStartTimeSet(false),
+ mStart(sequence),
+ mSequence(sequence),
+ mLast(sequence),
+ mIsMonotonic(isMonotonic) {
+ }
+
+ static int callback(const LogBufferElement* element, void* obj) {
+ LogFindStart* me = reinterpret_cast<LogFindStart*>(obj);
+ if ((!me->mPid || (me->mPid == element->getPid())) &&
+ (me->mLogMask & (1 << element->getLogId()))) {
+ log_time real = element->getRealTime();
+ if (me->mStart == real) {
+ me->mSequence = real;
+ me->mStartTimeSet = true;
+ return -1;
+ } else if (!me->mIsMonotonic || android::isMonotonic(real)) {
+ if (me->mStart < real) {
+ me->mSequence = me->mLast;
+ me->mStartTimeSet = true;
+ return -1;
+ }
+ me->mLast = real;
+ } else {
+ me->mLast = real;
+ }
+ }
+ return false;
+ }
+
+ bool found() {
+ return mStartTimeSet;
+ }
+
+ } logFindStart(pid, logMask, sequence,
+ logbuf().isMonotonic() && android::isMonotonic(start));
+
+ logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
+ FlushCommand::hasSecurityLogs(cli),
+ logFindStart.callback, &logFindStart);
+
+ if (!logFindStart.found()) {
+ doSocketDelete(cli);
+ return false;
+ }
+ }
+
+ android::prdebug(
+ "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+ "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+ cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
+ logMask, (int)pid, sequence.nsec(), timeout);
+
+ if (sequence == log_time::EPOCH) {
+ timeout = 0;
+ }
+
+ LogTimeEntry::wrlock();
+ auto entry = std::make_unique<LogTimeEntry>(
+ *this, cli, nonBlock, tail, logMask, pid, sequence, timeout);
+ if (!entry->startReader_Locked()) {
+ LogTimeEntry::unlock();
+ return false;
+ }
+
+ // release client and entry reference counts once done
+ cli->incRef();
+ mLogbuf.mTimes.emplace_front(std::move(entry));
+
+ // Set acceptable upper limit to wait for slow reader processing b/27242723
+ struct timeval t = { LOGD_SNDTIMEO, 0 };
+ setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
+ sizeof(t));
+
+ LogTimeEntry::unlock();
+
+ return true;
+}
+
+void LogReader::doSocketDelete(SocketClient* cli) {
+ LastLogTimes& times = mLogbuf.mTimes;
+ LogTimeEntry::wrlock();
+ LastLogTimes::iterator it = times.begin();
+ while (it != times.end()) {
+ LogTimeEntry* entry = it->get();
+ if (entry->mClient == cli) {
+ entry->release_Locked();
+ break;
+ }
+ it++;
+ }
+ LogTimeEntry::unlock();
+}
+
+int LogReader::getLogSocket() {
+ static const char socketName[] = "logdr";
+ int sock = android_get_control_socket(socketName);
+
+ if (sock < 0) {
+ sock = socket_local_server(
+ socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+ }
+
+ return sock;
+}
diff --git a/logd/LogReader.h b/logd/LogReader.h
new file mode 100644
index 000000000..b5312b60d
--- /dev/null
+++ b/logd/LogReader.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_WRITER_H__
+#define _LOGD_LOG_WRITER_H__
+
+#include <sysutils/SocketListener.h>
+
+#include "LogTimes.h"
+
+#define LOGD_SNDTIMEO 32
+
+class LogBuffer;
+
+class LogReader : public SocketListener {
+ LogBuffer& mLogbuf;
+
+ public:
+ explicit LogReader(LogBuffer* logbuf);
+ void notifyNewLog(log_mask_t logMask);
+
+ LogBuffer& logbuf(void) const {
+ return mLogbuf;
+ }
+
+ protected:
+ virtual bool onDataAvailable(SocketClient* cli);
+
+ private:
+ static int getLogSocket();
+
+ void doSocketDelete(SocketClient* cli);
+};
+
+#endif
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
new file mode 100644
index 000000000..431b7784b
--- /dev/null
+++ b/logd/LogStatistics.cpp
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <list>
+
+#include <private/android_logger.h>
+
+#include "LogStatistics.h"
+
+static const uint64_t hourSec = 60 * 60;
+static const uint64_t monthSec = 31 * 24 * hourSec;
+
+size_t LogStatistics::SizesTotal;
+
+LogStatistics::LogStatistics() : enable(false) {
+ log_time now(CLOCK_REALTIME);
+ log_id_for_each(id) {
+ mSizes[id] = 0;
+ mElements[id] = 0;
+ mDroppedElements[id] = 0;
+ mSizesTotal[id] = 0;
+ mElementsTotal[id] = 0;
+ mOldest[id] = now;
+ mNewest[id] = now;
+ mNewestDropped[id] = now;
+ }
+}
+
+namespace android {
+
+size_t sizesTotal() {
+ return LogStatistics::sizesTotal();
+}
+
+// caller must own and free character string
+char* pidToName(pid_t pid) {
+ char* retval = nullptr;
+ if (pid == 0) { // special case from auditd/klogd for kernel
+ retval = strdup("logd");
+ } else {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
+ int fd = open(buffer, O_RDONLY);
+ if (fd >= 0) {
+ ssize_t ret = read(fd, buffer, sizeof(buffer));
+ if (ret > 0) {
+ buffer[sizeof(buffer) - 1] = '\0';
+ // frameworks intermediate state
+ if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
+ retval = strdup(buffer);
+ }
+ }
+ close(fd);
+ }
+ }
+ return retval;
+}
+}
+
+void LogStatistics::addTotal(LogBufferElement* element) {
+ if (element->getDropped()) return;
+
+ log_id_t log_id = element->getLogId();
+ uint16_t size = element->getMsgLen();
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+}
+
+void LogStatistics::add(LogBufferElement* element) {
+ log_id_t log_id = element->getLogId();
+ uint16_t size = element->getMsgLen();
+ mSizes[log_id] += size;
+ ++mElements[log_id];
+
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
+ if (element->getDropped()) {
+ ++mDroppedElements[log_id];
+ } else {
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+ }
+
+ log_time stamp(element->getRealTime());
+ if (mNewest[log_id] < stamp) {
+ // A major time update invalidates the statistics :-(
+ log_time diff = stamp - mNewest[log_id];
+ mNewest[log_id] = stamp;
+
+ if (diff.tv_sec > hourSec) {
+ // approximate Do-Your-Best fixup
+ diff += mOldest[log_id];
+ if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
+ diff = stamp;
+ }
+ if (diff <= stamp) {
+ mOldest[log_id] = diff;
+ if (mNewestDropped[log_id] < diff) {
+ mNewestDropped[log_id] = diff;
+ }
+ }
+ }
+ }
+
+ if (log_id == LOG_ID_KERNEL) {
+ return;
+ }
+
+ uidTable[log_id].add(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].add(element->getPid(), element);
+ }
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.add(element->getPid(), element);
+ tidTable.add(element->getTid(), element);
+
+ uint32_t tag = element->getTag();
+ if (tag) {
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.add(tag, element);
+ } else {
+ tagTable.add(tag, element);
+ }
+ }
+
+ if (!element->getDropped()) {
+ tagNameTable.add(TagNameKey(element), element);
+ }
+}
+
+void LogStatistics::subtract(LogBufferElement* element) {
+ log_id_t log_id = element->getLogId();
+ uint16_t size = element->getMsgLen();
+ mSizes[log_id] -= size;
+ --mElements[log_id];
+ if (element->getDropped()) {
+ --mDroppedElements[log_id];
+ }
+
+ if (mOldest[log_id] < element->getRealTime()) {
+ mOldest[log_id] = element->getRealTime();
+ }
+
+ if (log_id == LOG_ID_KERNEL) {
+ return;
+ }
+
+ uidTable[log_id].subtract(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].subtract(element->getPid(), element);
+ }
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.subtract(element->getPid(), element);
+ tidTable.subtract(element->getTid(), element);
+
+ uint32_t tag = element->getTag();
+ if (tag) {
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.subtract(tag, element);
+ } else {
+ tagTable.subtract(tag, element);
+ }
+ }
+
+ if (!element->getDropped()) {
+ tagNameTable.subtract(TagNameKey(element), element);
+ }
+}
+
+// Atomically set an entry to drop
+// entry->setDropped(1) must follow this call, caller should do this explicitly.
+void LogStatistics::drop(LogBufferElement* element) {
+ log_id_t log_id = element->getLogId();
+ uint16_t size = element->getMsgLen();
+ mSizes[log_id] -= size;
+ ++mDroppedElements[log_id];
+
+ if (mNewestDropped[log_id] < element->getRealTime()) {
+ mNewestDropped[log_id] = element->getRealTime();
+ }
+
+ uidTable[log_id].drop(element->getUid(), element);
+ if (element->getUid() == AID_SYSTEM) {
+ pidSystemTable[log_id].drop(element->getPid(), element);
+ }
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.drop(element->getPid(), element);
+ tidTable.drop(element->getTid(), element);
+
+ uint32_t tag = element->getTag();
+ if (tag) {
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.drop(tag, element);
+ } else {
+ tagTable.drop(tag, element);
+ }
+ }
+
+ tagNameTable.subtract(TagNameKey(element), element);
+}
+
+// caller must own and free character string
+// Requires parent LogBuffer::wrlock() to be held
+const char* LogStatistics::uidToName(uid_t uid) const {
+ // Local hard coded favourites
+ if (uid == AID_LOGD) {
+ return strdup("auditd");
+ }
+
+ // Android system
+ if (uid < AID_APP) {
+ // in bionic, thread safe as long as we copy the results
+ struct passwd* pwd = getpwuid(uid);
+ if (pwd) {
+ return strdup(pwd->pw_name);
+ }
+ }
+
+ // Parse /data/system/packages.list
+ uid_t userId = uid % AID_USER_OFFSET;
+ const char* name = android::uidToName(userId);
+ if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
+ name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
+ }
+ if (name) {
+ return name;
+ }
+
+ // Android application
+ if (uid >= AID_APP) {
+ struct passwd* pwd = getpwuid(uid);
+ if (pwd) {
+ return strdup(pwd->pw_name);
+ }
+ }
+
+ // report uid -> pid(s) -> pidToName if unique
+ for (pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end();
+ ++it) {
+ const PidEntry& entry = it->second;
+
+ if (entry.getUid() == uid) {
+ const char* nameTmp = entry.getName();
+
+ if (nameTmp) {
+ if (!name) {
+ name = strdup(nameTmp);
+ } else if (fastcmp<strcmp>(name, nameTmp)) {
+ free(const_cast<char*>(name));
+ name = nullptr;
+ break;
+ }
+ }
+ }
+ }
+
+ // No one
+ return name;
+}
+
+std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(android::base::StringPrintf(name.c_str(),
+ android_log_id_to_name(id)),
+ std::string("Size"),
+ std::string(isprune ? "+/- Pruned" : "")) +
+ formatLine(std::string("UID PACKAGE"), std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
+}
+
+// Helper to truncate name, if too long, and add name dressings
+static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
+ std::string& name, std::string& size, size_t nameLen) {
+ const char* allocNameTmp = nullptr;
+ if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+ if (nameTmp) {
+ size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ free(const_cast<char*>(allocNameTmp));
+ }
+}
+
+std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%u", uid);
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ formatTmp(stat, nullptr, uid, name, size, 6);
+
+ std::string pruned = "";
+ if (worstUidEnabledForLogid(id)) {
+ size_t totalDropped = 0;
+ for (LogStatistics::uidTable_t::const_iterator it =
+ stat.uidTable[id].begin();
+ it != stat.uidTable[id].end(); ++it) {
+ totalDropped += it->second.getDropped();
+ }
+ size_t sizes = stat.sizes(id);
+ size_t totalSize = stat.sizesTotal(id);
+ size_t totalElements = stat.elementsTotal(id);
+ float totalVirtualSize =
+ (float)sizes + (float)totalDropped * totalSize / totalElements;
+ size_t entrySize = getSizes();
+ float virtualEntrySize = entrySize;
+ int realPermille = virtualEntrySize * 1000.0 / sizes;
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ virtualEntrySize += (float)dropped * totalSize / totalElements;
+ }
+ int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
+ int permille =
+ (realPermille - virtualPermille) * 1000L / (virtualPermille ?: 1);
+ if ((permille < -1) || (1 < permille)) {
+ std::string change;
+ const char* units = "%";
+ const char* prefix = (permille > 0) ? "+" : "";
+
+ if (permille > 999) {
+ permille = (permille + 1000) / 100; // Now tenths fold
+ units = "X";
+ prefix = "";
+ }
+ if ((-99 < permille) && (permille < 99)) {
+ change = android::base::StringPrintf(
+ "%s%d.%u%s", prefix, permille / 10,
+ ((permille < 0) ? (-permille % 10) : (permille % 10)),
+ units);
+ } else {
+ change = android::base::StringPrintf(
+ "%s%d%s", prefix, (permille + 5) / 10, units);
+ }
+ ssize_t spaces = EntryBaseConstants::pruned_len - 2 -
+ pruned.length() - change.length();
+ if ((spaces <= 0) && pruned.length()) {
+ spaces = 1;
+ }
+ if (spaces > 0) {
+ change += android::base::StringPrintf("%*s", (int)spaces, "");
+ }
+ pruned = change + pruned;
+ }
+ }
+
+ std::string output = formatLine(name, size, pruned);
+
+ if (uid != AID_SYSTEM) {
+ return output;
+ }
+
+ static const size_t maximum_sorted_entries = 32;
+ std::unique_ptr<const PidEntry* []> sorted =
+ stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
+
+ if (!sorted.get()) {
+ return output;
+ }
+ std::string byPid;
+ size_t index;
+ bool hasDropped = false;
+ for (index = 0; index < maximum_sorted_entries; ++index) {
+ const PidEntry* entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (getSizes() / 100)) {
+ break;
+ }
+ if (entry->getDropped()) {
+ hasDropped = true;
+ }
+ byPid += entry->format(stat, id);
+ }
+ if (index > 1) { // print this only if interesting
+ std::string ditto("\" ");
+ output += formatLine(std::string(" PID/UID COMMAND LINE"), ditto,
+ hasDropped ? ditto : std::string(""));
+ output += byPid;
+ }
+
+ return output;
+}
+
+std::string PidEntry::formatHeader(const std::string& name,
+ log_id_t /* id */) const {
+ return formatLine(name, std::string("Size"), std::string("Pruned")) +
+ formatLine(std::string(" PID/UID COMMAND LINE"),
+ std::string("BYTES"), std::string("NUM"));
+}
+
+std::string PidEntry::format(const LogStatistics& stat,
+ log_id_t /* id */) const {
+ uid_t uid = getUid();
+ pid_t pid = getPid();
+ std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ formatTmp(stat, getName(), uid, name, size, 12);
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TidEntry::formatHeader(const std::string& name,
+ log_id_t /* id */) const {
+ return formatLine(name, std::string("Size"), std::string("Pruned")) +
+ formatLine(std::string(" TID/UID COMM"), std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string TidEntry::format(const LogStatistics& stat,
+ log_id_t /* id */) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ formatTmp(stat, getName(), uid, name, size, 12);
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TagEntry::formatHeader(const std::string& name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(name, std::string("Size"),
+ std::string(isprune ? "Prune" : "")) +
+ formatLine(std::string(" TAG/UID TAGNAME"),
+ std::string("BYTES"), std::string(isprune ? "NUM" : ""));
+}
+
+std::string TagEntry::format(const LogStatistics& /* stat */,
+ log_id_t /* id */) const {
+ std::string name;
+ uid_t uid = getUid();
+ if (uid == (uid_t)-1) {
+ name = android::base::StringPrintf("%7u", getKey());
+ } else {
+ name = android::base::StringPrintf("%7u/%u", getKey(), uid);
+ }
+ const char* nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp);
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TagNameEntry::formatHeader(const std::string& name,
+ log_id_t /* id */) const {
+ return formatLine(name, std::string("Size"), std::string("")) +
+ formatLine(std::string(" TID/PID/UID LOG_TAG NAME"),
+ std::string("BYTES"), std::string(""));
+}
+
+std::string TagNameEntry::format(const LogStatistics& /* stat */,
+ log_id_t /* id */) const {
+ std::string name;
+ pid_t tid = getTid();
+ pid_t pid = getPid();
+ std::string pidstr;
+ if (pid != (pid_t)-1) {
+ pidstr = android::base::StringPrintf("%u", pid);
+ if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
+ }
+ int len = 9 - pidstr.length();
+ if (len < 0) len = 0;
+ if ((tid == (pid_t)-1) || (tid == pid)) {
+ name = android::base::StringPrintf("%*s", len, "");
+ } else {
+ name = android::base::StringPrintf("%*u", len, tid);
+ }
+ name += pidstr;
+ uid_t uid = getUid();
+ if (uid != (uid_t)-1) {
+ name += android::base::StringPrintf("/%u", uid);
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ const char* nameTmp = getName();
+ if (nameTmp) {
+ size_t lenSpace = std::max(16 - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ }
+
+ std::string pruned = "";
+
+ return formatLine(name, size, pruned);
+}
+
+static std::string formatMsec(uint64_t val) {
+ static const unsigned subsecDigits = 3;
+ static const uint64_t sec = MS_PER_SEC;
+
+ static const uint64_t minute = 60 * sec;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ std::string output;
+ if (val < sec) return output;
+
+ if (val >= day) {
+ output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ output += android::base::StringPrintf("%" PRIu64 ":",
+ (val / hour) % (day / hour));
+ }
+ output += android::base::StringPrintf(
+ (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ }
+ output +=
+ android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
+ (val / sec) % (minute / sec));
+ val %= sec;
+ unsigned digits = subsecDigits;
+ while (digits && ((val % 10) == 0)) {
+ val /= 10;
+ --digits;
+ }
+ if (digits) {
+ output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
+ }
+ return output;
+}
+
+std::string LogStatistics::format(uid_t uid, pid_t pid,
+ unsigned int logMask) const {
+ static const uint16_t spaces_total = 19;
+
+ // Report on total logging, current and for all time
+
+ std::string output = "size/num";
+ size_t oldLength;
+ int16_t spaces = 1;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+ oldLength = output.length();
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ android_log_id_to_name(id));
+ spaces += spaces_total + oldLength - output.length();
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*sTotal", spaces, "");
+
+ static const char TotalStr[] = "\nTotal";
+ spaces = 10 - strlen(TotalStr);
+ output += TotalStr;
+
+ size_t totalSize = 0;
+ size_t totalEls = 0;
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+ oldLength = output.length();
+ if (spaces < 0) spaces = 0;
+ size_t szs = sizesTotal(id);
+ totalSize += szs;
+ size_t els = elementsTotal(id);
+ totalEls += els;
+ output +=
+ android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
+ spaces += spaces_total + oldLength - output.length();
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
+ totalEls);
+
+ static const char NowStr[] = "\nNow";
+ spaces = 10 - strlen(NowStr);
+ output += NowStr;
+
+ totalSize = 0;
+ totalEls = 0;
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ size_t els = elements(id);
+ if (els) {
+ oldLength = output.length();
+ if (spaces < 0) spaces = 0;
+ size_t szs = sizes(id);
+ totalSize += szs;
+ totalEls += els;
+ output +=
+ android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
+ spaces -= output.length() - oldLength;
+ }
+ spaces += spaces_total;
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
+ totalEls);
+
+ static const char SpanStr[] = "\nLogspan";
+ spaces = 10 - strlen(SpanStr);
+ output += SpanStr;
+
+ // Total reports the greater of the individual maximum time span, or the
+ // validated minimum start and maximum end time span if it makes sense.
+ uint64_t minTime = UINT64_MAX;
+ uint64_t maxTime = 0;
+ uint64_t maxSpan = 0;
+ totalSize = 0;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ // validity checking
+ uint64_t oldest = mOldest[id].msec();
+ uint64_t newest = mNewest[id].msec();
+ if (newest <= oldest) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ uint64_t span = newest - oldest;
+ if (span > (monthSec * MS_PER_SEC)) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ // total span
+ if (minTime > oldest) minTime = oldest;
+ if (maxTime < newest) maxTime = newest;
+ if (span > maxSpan) maxSpan = span;
+ totalSize += span;
+
+ uint64_t dropped = mNewestDropped[id].msec();
+ if (dropped < oldest) dropped = oldest;
+ if (dropped > newest) dropped = newest;
+
+ oldLength = output.length();
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(span).c_str());
+ unsigned permille = ((newest - dropped) * 1000 + (span / 2)) / span;
+ if ((permille > 1) && (permille < 999)) {
+ output += android::base::StringPrintf("(%u", permille / 10);
+ permille %= 10;
+ if (permille) {
+ output += android::base::StringPrintf(".%u", permille);
+ }
+ output += android::base::StringPrintf("%%)");
+ }
+ spaces -= output.length() - oldLength;
+ spaces += spaces_total;
+ }
+ if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
+ (maxTime > maxSpan)) {
+ maxSpan = maxTime;
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(maxSpan).c_str());
+
+ static const char OverheadStr[] = "\nOverhead";
+ spaces = 10 - strlen(OverheadStr);
+ output += OverheadStr;
+
+ totalSize = 0;
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ size_t els = elements(id);
+ if (els) {
+ oldLength = output.length();
+ if (spaces < 0) spaces = 0;
+ // estimate the std::list overhead.
+ static const size_t overhead =
+ ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
+ -sizeof(uint64_t)) +
+ sizeof(std::list<LogBufferElement*>);
+ size_t szs = sizes(id) + els * overhead;
+ totalSize += szs;
+ output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
+ spaces -= output.length() - oldLength;
+ }
+ spaces += spaces_total;
+ }
+ totalSize += sizeOf();
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
+
+ // Report on Chattiest
+
+ std::string name;
+
+ // Chattiest by application (UID)
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
+ : "Logging for your UID in %s log buffer:";
+ output += uidTable[id].format(*this, uid, pid, name, id);
+ }
+
+ if (enable) {
+ name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
+ : "Logging for this PID:";
+ output += pidTable.format(*this, uid, pid, name);
+ name = "Chattiest TIDs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tidTable.format(*this, uid, pid, name);
+ }
+
+ if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
+ name = "Chattiest events log buffer TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
+ }
+
+ if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
+ name = "Chattiest security log buffer TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output +=
+ securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
+ }
+
+ if (enable) {
+ name = "Chattiest TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tagNameTable.format(*this, uid, pid, name);
+ }
+
+ return output;
+}
+
+namespace android {
+
+uid_t pidToUid(pid_t pid) {
+ char buffer[512];
+ snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
+ FILE* fp = fopen(buffer, "r");
+ if (fp) {
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ int uid = AID_LOGD;
+ char space = 0;
+ if ((sscanf(buffer, "Uid: %d%c", &uid, &space) == 2) &&
+ isspace(space)) {
+ fclose(fp);
+ return uid;
+ }
+ }
+ fclose(fp);
+ }
+ return AID_LOGD; // associate this with the logger
+}
+}
+
+uid_t LogStatistics::pidToUid(pid_t pid) {
+ return pidTable.add(pid)->second.getUid();
+}
+
+// caller must free character string
+const char* LogStatistics::pidToName(pid_t pid) const {
+ // An inconvenient truth ... getName() can alter the object
+ pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
+ const char* name = writablePidTable.add(pid)->second.getName();
+ if (!name) {
+ return nullptr;
+ }
+ return strdup(name);
+}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
new file mode 100644
index 000000000..0782de3d1
--- /dev/null
+++ b/logd/LogStatistics.h
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_STATISTICS_H__
+#define _LOGD_LOG_STATISTICS_H__
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <algorithm> // std::max
+#include <memory>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <log/log_time.h>
+#include <private/android_filesystem_config.h>
+#include <utils/FastStrcmp.h>
+
+#include "LogBufferElement.h"
+#include "LogUtils.h"
+
+#define log_id_for_each(i) \
+ for (log_id_t i = LOG_ID_MIN; (i) < LOG_ID_MAX; (i) = (log_id_t)((i) + 1))
+
+class LogStatistics;
+
+template <typename TKey, typename TEntry>
+class LogHashtable {
+ std::unordered_map<TKey, TEntry> map;
+
+ size_t bucket_size() const {
+ size_t count = 0;
+ for (size_t idx = 0; idx < map.bucket_count(); ++idx) {
+ size_t bucket_size = map.bucket_size(idx);
+ if (bucket_size == 0) bucket_size = 1;
+ count += bucket_size;
+ }
+ float load_factor = map.max_load_factor();
+ if (load_factor < 1.0) return count;
+ return count * load_factor;
+ }
+
+ static const size_t unordered_map_per_entry_overhead = sizeof(void*);
+ static const size_t unordered_map_bucket_overhead = sizeof(void*);
+
+ public:
+ size_t size() const {
+ return map.size();
+ }
+
+ // Estimate unordered_map memory usage.
+ size_t sizeOf() const {
+ return sizeof(*this) +
+ (size() * (sizeof(TEntry) + unordered_map_per_entry_overhead)) +
+ (bucket_size() * sizeof(size_t) + unordered_map_bucket_overhead);
+ }
+
+ typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+ typedef
+ typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
+
+ std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
+ size_t len) const {
+ if (!len) {
+ std::unique_ptr<const TEntry* []> sorted(nullptr);
+ return sorted;
+ }
+
+ const TEntry** retval = new const TEntry*[len];
+ memset(retval, 0, sizeof(*retval) * len);
+
+ for (const_iterator it = map.begin(); it != map.end(); ++it) {
+ const TEntry& entry = it->second;
+
+ if ((uid != AID_ROOT) && (uid != entry.getUid())) {
+ continue;
+ }
+ if (pid && entry.getPid() && (pid != entry.getPid())) {
+ continue;
+ }
+
+ size_t sizes = entry.getSizes();
+ ssize_t index = len - 1;
+ while ((!retval[index] || (sizes > retval[index]->getSizes())) &&
+ (--index >= 0))
+ ;
+ if (++index < (ssize_t)len) {
+ size_t num = len - index - 1;
+ if (num) {
+ memmove(&retval[index + 1], &retval[index],
+ num * sizeof(retval[0]));
+ }
+ retval[index] = &entry;
+ }
+ }
+ std::unique_ptr<const TEntry* []> sorted(retval);
+ return sorted;
+ }
+
+ inline iterator add(const TKey& key, const LogBufferElement* element) {
+ iterator it = map.find(key);
+ if (it == map.end()) {
+ it = map.insert(std::make_pair(key, TEntry(element))).first;
+ } else {
+ it->second.add(element);
+ }
+ return it;
+ }
+
+ inline iterator add(TKey key) {
+ iterator it = map.find(key);
+ if (it == map.end()) {
+ it = map.insert(std::make_pair(key, TEntry(key))).first;
+ } else {
+ it->second.add(key);
+ }
+ return it;
+ }
+
+ void subtract(TKey&& key, const LogBufferElement* element) {
+ iterator it = map.find(std::move(key));
+ if ((it != map.end()) && it->second.subtract(element)) {
+ map.erase(it);
+ }
+ }
+
+ void subtract(const TKey& key, const LogBufferElement* element) {
+ iterator it = map.find(key);
+ if ((it != map.end()) && it->second.subtract(element)) {
+ map.erase(it);
+ }
+ }
+
+ inline void drop(TKey key, const LogBufferElement* element) {
+ iterator it = map.find(key);
+ if (it != map.end()) {
+ it->second.drop(element);
+ }
+ }
+
+ inline iterator begin() {
+ return map.begin();
+ }
+ inline const_iterator begin() const {
+ return map.begin();
+ }
+ inline iterator end() {
+ return map.end();
+ }
+ inline const_iterator end() const {
+ return map.end();
+ }
+
+ std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
+ const std::string& name = std::string(""),
+ log_id_t id = LOG_ID_MAX) const {
+ static const size_t maximum_sorted_entries = 32;
+ std::string output;
+ std::unique_ptr<const TEntry* []> sorted =
+ sort(uid, pid, maximum_sorted_entries);
+ if (!sorted.get()) {
+ return output;
+ }
+ bool headerPrinted = false;
+ for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+ const TEntry* entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+ break;
+ }
+ if (!headerPrinted) {
+ output += "\n\n";
+ output += entry->formatHeader(name, id);
+ headerPrinted = true;
+ }
+ output += entry->format(stat, id);
+ }
+ return output;
+ }
+};
+
+namespace EntryBaseConstants {
+static constexpr size_t pruned_len = 14;
+static constexpr size_t total_len = 80;
+}
+
+struct EntryBase {
+ size_t size;
+
+ EntryBase() : size(0) {
+ }
+ explicit EntryBase(const LogBufferElement* element)
+ : size(element->getMsgLen()) {
+ }
+
+ size_t getSizes() const {
+ return size;
+ }
+
+ inline void add(const LogBufferElement* element) {
+ size += element->getMsgLen();
+ }
+ inline bool subtract(const LogBufferElement* element) {
+ size -= element->getMsgLen();
+ return !size;
+ }
+
+ static std::string formatLine(const std::string& name,
+ const std::string& size,
+ const std::string& pruned) {
+ ssize_t drop_len =
+ std::max(pruned.length() + 1, EntryBaseConstants::pruned_len);
+ ssize_t size_len =
+ std::max(size.length() + 1, EntryBaseConstants::total_len -
+ name.length() - drop_len - 1);
+
+ std::string ret = android::base::StringPrintf(
+ "%s%*s%*s", name.c_str(), (int)size_len, size.c_str(),
+ (int)drop_len, pruned.c_str());
+ // remove any trailing spaces
+ size_t pos = ret.size();
+ size_t len = 0;
+ while (pos && isspace(ret[--pos])) ++len;
+ if (len) ret.erase(pos + 1, len);
+ return ret + "\n";
+ }
+};
+
+struct EntryBaseDropped : public EntryBase {
+ size_t dropped;
+
+ EntryBaseDropped() : dropped(0) {
+ }
+ explicit EntryBaseDropped(const LogBufferElement* element)
+ : EntryBase(element), dropped(element->getDropped()) {
+ }
+
+ size_t getDropped() const {
+ return dropped;
+ }
+
+ inline void add(const LogBufferElement* element) {
+ dropped += element->getDropped();
+ EntryBase::add(element);
+ }
+ inline bool subtract(const LogBufferElement* element) {
+ dropped -= element->getDropped();
+ return EntryBase::subtract(element) && !dropped;
+ }
+ inline void drop(const LogBufferElement* element) {
+ dropped += 1;
+ EntryBase::subtract(element);
+ }
+};
+
+struct UidEntry : public EntryBaseDropped {
+ const uid_t uid;
+ pid_t pid;
+
+ explicit UidEntry(const LogBufferElement* element)
+ : EntryBaseDropped(element),
+ uid(element->getUid()),
+ pid(element->getPid()) {
+ }
+
+ inline const uid_t& getKey() const {
+ return uid;
+ }
+ inline const uid_t& getUid() const {
+ return getKey();
+ }
+ inline const pid_t& getPid() const {
+ return pid;
+ }
+
+ inline void add(const LogBufferElement* element) {
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
+namespace android {
+uid_t pidToUid(pid_t pid);
+}
+
+struct PidEntry : public EntryBaseDropped {
+ const pid_t pid;
+ uid_t uid;
+ char* name;
+
+ explicit PidEntry(pid_t pid)
+ : EntryBaseDropped(),
+ pid(pid),
+ uid(android::pidToUid(pid)),
+ name(android::pidToName(pid)) {
+ }
+ explicit PidEntry(const LogBufferElement* element)
+ : EntryBaseDropped(element),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(android::pidToName(pid)) {
+ }
+ PidEntry(const PidEntry& element)
+ : EntryBaseDropped(element),
+ pid(element.pid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : nullptr) {
+ }
+ ~PidEntry() {
+ free(name);
+ }
+
+ const pid_t& getKey() const {
+ return pid;
+ }
+ const pid_t& getPid() const {
+ return getKey();
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return name;
+ }
+
+ inline void add(pid_t newPid) {
+ if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
+ free(name);
+ name = nullptr;
+ }
+ if (!name) {
+ name = android::pidToName(newPid);
+ }
+ }
+
+ inline void add(const LogBufferElement* element) {
+ uid_t incomingUid = element->getUid();
+ if (getUid() != incomingUid) {
+ uid = incomingUid;
+ free(name);
+ name = android::pidToName(element->getPid());
+ } else {
+ add(element->getPid());
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
+struct TidEntry : public EntryBaseDropped {
+ const pid_t tid;
+ pid_t pid;
+ uid_t uid;
+ char* name;
+
+ TidEntry(pid_t tid, pid_t pid)
+ : EntryBaseDropped(),
+ tid(tid),
+ pid(pid),
+ uid(android::pidToUid(tid)),
+ name(android::tidToName(tid)) {
+ }
+ explicit TidEntry(const LogBufferElement* element)
+ : EntryBaseDropped(element),
+ tid(element->getTid()),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(android::tidToName(tid)) {
+ }
+ TidEntry(const TidEntry& element)
+ : EntryBaseDropped(element),
+ tid(element.tid),
+ pid(element.pid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : nullptr) {
+ }
+ ~TidEntry() {
+ free(name);
+ }
+
+ const pid_t& getKey() const {
+ return tid;
+ }
+ const pid_t& getTid() const {
+ return getKey();
+ }
+ const pid_t& getPid() const {
+ return pid;
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return name;
+ }
+
+ inline void add(pid_t incomingTid) {
+ if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
+ free(name);
+ name = nullptr;
+ }
+ if (!name) {
+ name = android::tidToName(incomingTid);
+ }
+ }
+
+ inline void add(const LogBufferElement* element) {
+ uid_t incomingUid = element->getUid();
+ pid_t incomingPid = element->getPid();
+ if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
+ uid = incomingUid;
+ pid = incomingPid;
+ free(name);
+ name = android::tidToName(element->getTid());
+ } else {
+ add(element->getTid());
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
+struct TagEntry : public EntryBaseDropped {
+ const uint32_t tag;
+ pid_t pid;
+ uid_t uid;
+
+ explicit TagEntry(const LogBufferElement* element)
+ : EntryBaseDropped(element),
+ tag(element->getTag()),
+ pid(element->getPid()),
+ uid(element->getUid()) {
+ }
+
+ const uint32_t& getKey() const {
+ return tag;
+ }
+ const pid_t& getPid() const {
+ return pid;
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return android::tagToName(tag);
+ }
+
+ inline void add(const LogBufferElement* element) {
+ if (uid != element->getUid()) {
+ uid = -1;
+ }
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ EntryBaseDropped::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
+struct TagNameKey {
+ std::string* alloc;
+ std::string_view name; // Saves space if const char*
+
+ explicit TagNameKey(const LogBufferElement* element)
+ : alloc(nullptr), name("", strlen("")) {
+ if (element->isBinary()) {
+ uint32_t tag = element->getTag();
+ if (tag) {
+ const char* cp = android::tagToName(tag);
+ if (cp) {
+ name = std::string_view(cp, strlen(cp));
+ return;
+ }
+ }
+ alloc = new std::string(
+ android::base::StringPrintf("[%" PRIu32 "]", tag));
+ if (!alloc) return;
+ name = std::string_view(alloc->c_str(), alloc->size());
+ return;
+ }
+ const char* msg = element->getMsg();
+ if (!msg) {
+ name = std::string_view("chatty", strlen("chatty"));
+ return;
+ }
+ ++msg;
+ uint16_t len = element->getMsgLen();
+ len = (len <= 1) ? 0 : strnlen(msg, len - 1);
+ if (!len) {
+ name = std::string_view("<NULL>", strlen("<NULL>"));
+ return;
+ }
+ alloc = new std::string(msg, len);
+ if (!alloc) return;
+ name = std::string_view(alloc->c_str(), alloc->size());
+ }
+
+ explicit TagNameKey(TagNameKey&& rval) noexcept
+ : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
+ rval.alloc = nullptr;
+ }
+
+ explicit TagNameKey(const TagNameKey& rval)
+ : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
+ name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
+ }
+
+ ~TagNameKey() {
+ if (alloc) delete alloc;
+ }
+
+ operator const std::string_view() const {
+ return name;
+ }
+
+ const char* data() const {
+ return name.data();
+ }
+ size_t length() const {
+ return name.length();
+ }
+
+ bool operator==(const TagNameKey& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!=(const TagNameKey& rval) const {
+ return !(*this == rval);
+ }
+
+ size_t getAllocLength() const {
+ return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
+ }
+};
+
+// Hash for TagNameKey
+template <>
+struct std::hash<TagNameKey>
+ : public std::unary_function<const TagNameKey&, size_t> {
+ size_t operator()(const TagNameKey& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::string_view>()(std::string_view(__t));
+ }
+};
+
+struct TagNameEntry : public EntryBase {
+ pid_t tid;
+ pid_t pid;
+ uid_t uid;
+ TagNameKey name;
+
+ explicit TagNameEntry(const LogBufferElement* element)
+ : EntryBase(element),
+ tid(element->getTid()),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(element) {
+ }
+
+ const TagNameKey& getKey() const {
+ return name;
+ }
+ const pid_t& getTid() const {
+ return tid;
+ }
+ const pid_t& getPid() const {
+ return pid;
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return name.data();
+ }
+ size_t getNameAllocLength() const {
+ return name.getAllocLength();
+ }
+
+ inline void add(const LogBufferElement* element) {
+ if (uid != element->getUid()) {
+ uid = -1;
+ }
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ if (tid != element->getTid()) {
+ tid = -1;
+ }
+ EntryBase::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
+template <typename TEntry>
+class LogFindWorst {
+ std::unique_ptr<const TEntry* []> sorted;
+
+ public:
+ explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
+ : sorted(std::move(sorted)) {
+ }
+
+ void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
+ size_t threshold) {
+ if (sorted.get() && sorted[0] && sorted[1]) {
+ worst_sizes = sorted[0]->getSizes();
+ if ((worst_sizes > threshold)
+ // Allow time horizon to extend roughly tenfold, assume
+ // average entry length is 100 characters.
+ && (worst_sizes > (10 * sorted[0]->getDropped()))) {
+ worst = sorted[0]->getKey();
+ second_worst_sizes = sorted[1]->getSizes();
+ if (second_worst_sizes < threshold) {
+ second_worst_sizes = threshold;
+ }
+ }
+ }
+ }
+
+ void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
+ if (sorted.get() && sorted[0] && sorted[1]) {
+ worst = sorted[0]->getKey();
+ second_worst_sizes =
+ worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
+ }
+ }
+};
+
+// Log Statistics
+class LogStatistics {
+ friend UidEntry;
+
+ size_t mSizes[LOG_ID_MAX];
+ size_t mElements[LOG_ID_MAX];
+ size_t mDroppedElements[LOG_ID_MAX];
+ size_t mSizesTotal[LOG_ID_MAX];
+ size_t mElementsTotal[LOG_ID_MAX];
+ log_time mOldest[LOG_ID_MAX];
+ log_time mNewest[LOG_ID_MAX];
+ log_time mNewestDropped[LOG_ID_MAX];
+ static size_t SizesTotal;
+ bool enable;
+
+ // uid to size list
+ typedef LogHashtable<uid_t, UidEntry> uidTable_t;
+ uidTable_t uidTable[LOG_ID_MAX];
+
+ // pid of system to size list
+ typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
+ pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+
+ // pid to uid list
+ typedef LogHashtable<pid_t, PidEntry> pidTable_t;
+ pidTable_t pidTable;
+
+ // tid to uid list
+ typedef LogHashtable<pid_t, TidEntry> tidTable_t;
+ tidTable_t tidTable;
+
+ // tag list
+ typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
+ tagTable_t tagTable;
+
+ // security tag list
+ tagTable_t securityTagTable;
+
+ // global tag list
+ typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
+ tagNameTable_t tagNameTable;
+
+ size_t sizeOf() const {
+ size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
+ tagTable.sizeOf() + securityTagTable.sizeOf() +
+ tagNameTable.sizeOf() +
+ (pidTable.size() * sizeof(pidTable_t::iterator)) +
+ (tagTable.size() * sizeof(tagTable_t::iterator));
+ for (auto it : pidTable) {
+ const char* name = it.second.getName();
+ if (name) size += strlen(name) + 1;
+ }
+ for (auto it : tidTable) {
+ const char* name = it.second.getName();
+ if (name) size += strlen(name) + 1;
+ }
+ for (auto it : tagNameTable) size += it.second.getNameAllocLength();
+ log_id_for_each(id) {
+ size += uidTable[id].sizeOf();
+ size += uidTable[id].size() * sizeof(uidTable_t::iterator);
+ size += pidSystemTable[id].sizeOf();
+ size +=
+ pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
+ }
+ return size;
+ }
+
+ public:
+ LogStatistics();
+
+ void enableStatistics() {
+ enable = true;
+ }
+
+ void addTotal(LogBufferElement* entry);
+ void add(LogBufferElement* entry);
+ void subtract(LogBufferElement* entry);
+ // entry->setDropped(1) must follow this call
+ void drop(LogBufferElement* entry);
+ // Correct for coalescing two entries referencing dropped content
+ void erase(LogBufferElement* element) {
+ log_id_t log_id = element->getLogId();
+ --mElements[log_id];
+ --mDroppedElements[log_id];
+ }
+
+ LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
+ return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
+ }
+ LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
+ log_id id) {
+ return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
+ }
+ LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
+ return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
+ }
+
+ // fast track current value by id only
+ size_t sizes(log_id_t id) const {
+ return mSizes[id];
+ }
+ size_t elements(log_id_t id) const {
+ return mElements[id];
+ }
+ size_t realElements(log_id_t id) const {
+ return mElements[id] - mDroppedElements[id];
+ }
+ size_t sizesTotal(log_id_t id) const {
+ return mSizesTotal[id];
+ }
+ size_t elementsTotal(log_id_t id) const {
+ return mElementsTotal[id];
+ }
+ static size_t sizesTotal() {
+ return SizesTotal;
+ }
+
+ std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
+
+ // helper (must be locked directly or implicitly by mLogElementsLock)
+ const char* pidToName(pid_t pid) const;
+ uid_t pidToUid(pid_t pid);
+ const char* uidToName(uid_t uid) const;
+};
+
+#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 000000000..0cc7886ea
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,903 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+using android::base::make_scope_guard;
+
+static LogTags* logtags;
+
+const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
+const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags";
+// Only for debug
+const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags";
+
+// Sniff for first uid=%d in utf8z comment string
+static uid_t sniffUid(const char* comment, const char* endp) {
+ if (!comment) return AID_ROOT;
+
+ if (*comment == '#') ++comment;
+ while ((comment < endp) && (*comment != '\n') && isspace(*comment))
+ ++comment;
+ static const char uid_str[] = "uid=";
+ if (((comment + strlen(uid_str)) >= endp) ||
+ fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) ||
+ !isdigit(comment[strlen(uid_str)]))
+ return AID_ROOT;
+ char* cp;
+ unsigned long Uid = strtoul(comment + 4, &cp, 10);
+ if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT;
+
+ return Uid;
+}
+
+// Checks for file corruption, and report false if there was no need
+// to rebuild the referenced file. Failure to rebuild is only logged,
+// does not cause a return value of false.
+bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) {
+ int fd;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ if (tag2total.begin() == tag2total.end()) {
+ return false;
+ }
+
+ file2watermark_const_iterator iwater = file2watermark.find(filename);
+ if (iwater == file2watermark.end()) {
+ return false;
+ }
+
+ struct stat sb;
+ if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) {
+ return false;
+ }
+
+ // dump what we already know back into the file?
+ fd = TEMP_FAILURE_RETRY(open(
+ filename, O_WRONLY | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
+ if (fd >= 0) {
+ time_t now = time(nullptr);
+ struct tm tm;
+ localtime_r(&now, &tm);
+ char timebuf[20];
+ size_t len =
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
+ android::base::WriteStringToFd(
+ android::base::StringPrintf(
+ "# Rebuilt %.20s, content owned by logd\n", timebuf),
+ fd);
+ for (const auto& it : tag2total) {
+ android::base::WriteStringToFd(
+ formatEntry_locked(it.first, AID_ROOT), fd);
+ }
+ close(fd);
+ }
+ }
+
+ if (warn) {
+ android::prdebug(
+ ((fd < 0) ? "%s failed to rebuild"
+ : "%s missing, damaged or truncated; rebuilt"),
+ filename);
+ }
+
+ if (fd >= 0) {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ struct stat sb;
+ if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size;
+ }
+
+ return true;
+}
+
+void LogTags::AddEventLogTags(uint32_t tag, uid_t uid, const std::string& Name,
+ const std::string& Format, const char* source,
+ bool warn) {
+ std::string Key = Name;
+ if (Format.length()) Key += "+" + Format;
+
+ bool update = !source || !!strcmp(source, system_event_log_tags);
+ bool newOne;
+
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+
+ // unlikely except for dupes, or updates to uid list (more later)
+ if (itot != tag2total.end()) update = false;
+
+ newOne = tag2name.find(tag) == tag2name.end();
+ key2tag[Key] = tag;
+
+ if (Format.length()) {
+ if (key2tag.find(Name) == key2tag.end()) {
+ key2tag[Name] = tag;
+ }
+ tag2format[tag] = Format;
+ }
+ tag2name[tag] = Name;
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut != tag2uid.end()) {
+ if (uid == AID_ROOT) {
+ tag2uid.erase(ut);
+ update = true;
+ } else if (ut->second.find(uid) == ut->second.end()) {
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ update = true;
+ }
+ } else if (newOne && (uid != AID_ROOT)) {
+ tag2uid[tag].emplace(uid);
+ update = true;
+ }
+
+ // updatePersist -> trigger output on modified
+ // content, reset tag2total if available
+ if (update && (itot != tag2total.end())) tag2total[tag] = 0;
+ }
+
+ if (update) {
+ WritePersistEventLogTags(tag, uid, source);
+ } else if (warn && !newOne && source) {
+ // For the files, we want to report dupes.
+ android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag, Name.c_str(),
+ Format.c_str(), source);
+ }
+}
+
+// Read the event log tags file, and build up our internal database
+void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
+ bool etc = !strcmp(filename, system_event_log_tags);
+ bool debug = !etc && !strcmp(filename, debug_event_log_tags);
+
+ if (!etc) {
+ RebuildFileEventLogTags(filename, warn);
+ }
+ std::string content;
+ if (android::base::ReadFileToString(filename, &content)) {
+ char* cp = (char*)content.c_str();
+ char* endp = cp + content.length();
+
+ {
+ android::RWLock::AutoRLock writeLock(rwlock);
+
+ file2watermark[filename] = content.length();
+ }
+
+ char* lineStart = cp;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = cp;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ /* comment; just scan to end */
+ lineStart = nullptr;
+ } else if (isdigit(*cp)) {
+ unsigned long Tag = strtoul(cp, &cp, 10);
+ if (warn && (Tag > emptyTag)) {
+ android::prdebug("tag too large %lu", Tag);
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ if (cp >= endp) break;
+ if (*cp == '\n') continue;
+ const char* name = cp;
+ /* Determine whether it is a valid tag name [a-zA-Z0-9_] */
+ bool hasAlpha = false;
+ while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) {
+ if (!isdigit(*cp)) hasAlpha = true;
+ ++cp;
+ }
+ std::string Name(name, cp - name);
+#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
+ static const size_t maximum_official_tag_name_size = 24;
+ if (warn &&
+ (Name.length() > maximum_official_tag_name_size)) {
+ android::prdebug("tag name too long %s", Name.c_str());
+ }
+#endif
+ if (hasAlpha &&
+ ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
+ if (Tag > emptyTag) {
+ if (*cp != '\n') lineStart = nullptr;
+ continue;
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp))
+ ++cp;
+ const char* format = cp;
+ uid_t uid = AID_ROOT;
+ while ((cp < endp) && (*cp != '\n')) {
+ if (*cp == '#') {
+ uid = sniffUid(cp, endp);
+ lineStart = nullptr;
+ break;
+ }
+ ++cp;
+ }
+ while ((cp > format) && isspace(cp[-1])) {
+ --cp;
+ lineStart = nullptr;
+ }
+ std::string Format(format, cp - format);
+
+ AddEventLogTags((uint32_t)Tag, uid, Name, Format,
+ filename, warn);
+ } else {
+ if (warn) {
+ android::prdebug("tag name invalid %.*s",
+ (int)(cp - name + 1), name);
+ }
+ lineStart = nullptr;
+ }
+ } else if (!isspace(*cp)) {
+ break;
+ }
+ }
+ cp++;
+ }
+ } else if (warn) {
+ android::prdebug("Cannot read %s", filename);
+ }
+}
+
+// Extract a 4-byte value from a byte stream.
+static inline uint32_t get4LE(const char* msg) {
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(msg);
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+// Additional persistent sources for invented log tags. Read the
+// special pmsg event for log tags, and build up our internal
+// database with any found.
+void LogTags::ReadPersistEventLogTags() {
+ struct logger_list* logger_list = android_logger_list_alloc(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK, 0,
+ (pid_t)0);
+ if (!logger_list) return;
+
+ struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
+ struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY);
+ if (!e && !s) {
+ android_logger_list_free(logger_list);
+ return;
+ }
+
+ for (;;) {
+ struct log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ if (ret <= 0) break;
+
+ const char* msg = log_msg.msg();
+ if (!msg) continue;
+ if (log_msg.entry.len <= sizeof(uint32_t)) continue;
+ uint32_t Tag = get4LE(msg);
+ if (Tag != TAG_DEF_LOG_TAG) continue;
+ uid_t uid = log_msg.entry.uid;
+
+ std::string Name;
+ std::string Format;
+ android_log_list_element elem;
+ {
+ auto ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t));
+ auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_LIST) {
+ continue;
+ }
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_INT) {
+ continue;
+ }
+ Tag = elem.data.int32;
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Name = std::string(elem.data.string, elem.len);
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Format = std::string(elem.data.string, elem.len);
+ elem = android_log_read_next(ctx);
+ }
+ if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
+
+ AddEventLogTags(Tag, uid, Name, Format);
+ }
+ android_logger_list_free(logger_list);
+}
+
+LogTags::LogTags() {
+ ReadFileEventLogTags(system_event_log_tags);
+ // Following will likely fail on boot, but is required if logd restarts
+ ReadFileEventLogTags(dynamic_event_log_tags, false);
+ if (__android_log_is_debuggable()) {
+ ReadFileEventLogTags(debug_event_log_tags, false);
+ }
+ ReadPersistEventLogTags();
+
+ logtags = this;
+}
+
+// Converts an event tag into a name
+const char* LogTags::tagToName(uint32_t tag) const {
+ tag2name_const_iterator it;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ it = tag2name.find(tag);
+ if ((it == tag2name.end()) || (it->second.length() == 0)) return nullptr;
+
+ return it->second.c_str();
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This must be a pure reader to our database, as everything else is
+// guaranteed single-threaded except this access point which is
+// asynchonous and can be multithreaded and thus rentrant. The
+// object's rwlock is only used to guarantee atomic access to the
+// unordered_map to prevent corruption, with a requirement to be a
+// low chance of contention for this call. If we end up changing
+// this algorithm resulting in write, then we should use a different
+// lock than the object's rwlock to protect groups of associated
+// actions.
+const char* android::tagToName(uint32_t tag) {
+ LogTags* me = logtags;
+
+ if (!me) return nullptr;
+ me->WritePmsgEventLogTags(tag);
+ return me->tagToName(tag);
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This only works on userdebug and eng devices to re-read the
+// /data/misc/logd/event-log-tags file right after /data is mounted.
+// The operation is near to boot and should only happen once. There
+// are races associated with its use since it can trigger a Rebuild
+// of the file, but that is a can-not-happen since the file was not
+// read yet. More dangerous if called later, but if all is well it
+// should just skip over everything and not write any new entries.
+void android::ReReadEventLogTags() {
+ LogTags* me = logtags;
+
+ if (me && __android_log_is_debuggable()) {
+ me->ReadFileEventLogTags(me->debug_event_log_tags);
+ }
+}
+
+// converts an event tag into a format
+const char* LogTags::tagToFormat(uint32_t tag) const {
+ tag2format_const_iterator iform;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ iform = tag2format.find(tag);
+ if (iform == tag2format.end()) return nullptr;
+
+ return iform->second.c_str();
+}
+
+// converts a name into an event tag
+uint32_t LogTags::nameToTag(const char* name) const {
+ uint32_t ret = emptyTag;
+
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ key2tag_const_iterator ik = key2tag.find(std::string(name));
+ if (ik != key2tag.end()) ret = ik->second;
+
+ return ret;
+}
+
+// Caller must perform locks, can be under reader (for pre-check) or
+// writer lock. We use this call to invent a new deterministically
+// random tag, unique is cleared if no conflicts. If format is NULL,
+// we are in readonly mode.
+uint32_t LogTags::nameToTag_locked(const std::string& name, const char* format,
+ bool& unique) {
+ key2tag_const_iterator ik;
+
+ bool write = format != nullptr;
+ unique = write;
+
+ if (!write) {
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+ ik = key2tag.find(name);
+ if (ik == key2tag.end()) return emptyTag;
+ return ik->second;
+ }
+
+ std::string Key(name);
+ if (*format) Key += std::string("+") + format;
+
+ ik = key2tag.find(Key);
+ if (ik != key2tag.end()) {
+ unique = false;
+ return ik->second;
+ }
+
+ size_t Hash = key2tag.hash_function()(Key);
+ uint32_t Tag = Hash;
+ // This sets an upper limit on the conflics we are allowed to deal with.
+ for (unsigned i = 0; i < 256;) {
+ tag2name_const_iterator it = tag2name.find(Tag);
+ if (it == tag2name.end()) return Tag;
+ std::string localKey(it->second);
+ tag2format_const_iterator iform = tag2format.find(Tag);
+ if ((iform == tag2format.end()) && iform->second.length()) {
+ localKey += "+" + iform->second;
+ }
+ unique = !!it->second.compare(localKey);
+ if (!unique) return Tag; // unlikely except in a race
+
+ ++i;
+ // Algorithm to convert hash to next tag
+ if (i < 32) {
+ Tag = (Hash >> i);
+ // size_t is 32 bits, or upper word zero, rotate
+ if ((sizeof(Hash) <= 4) || ((Hash & (uint64_t(-1LL) << 32)) == 0)) {
+ Tag |= Hash << (32 - i);
+ }
+ } else {
+ Tag = Hash + i - 31;
+ }
+ }
+ return emptyTag;
+}
+
+static int openFile(const char* name, int mode, bool warning) {
+ int fd = TEMP_FAILURE_RETRY(open(name, mode));
+ if ((fd < 0) && warning) {
+ android::prdebug("Failed open %s (%d)", name, errno);
+ }
+ return fd;
+}
+
+void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot == tag2total.end()) return; // source is a static entry
+
+ size_t lastTotal = itot->second;
+
+ // Every 16K (half the smallest configurable pmsg buffer size) record
+ static const size_t rate_to_pmsg = 16 * 1024;
+ if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+ return;
+ }
+
+ static int pmsg_fd = -1;
+ if (pmsg_fd < 0) {
+ pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ // unlikely, but deal with partners with borken pmsg
+ if (pmsg_fd < 0) return;
+ }
+
+ std::string Name = tag2name[tag];
+ tag2format_const_iterator iform = tag2format.find(tag);
+ std::string Format = (iform != tag2format.end()) ? iform->second : "";
+
+ auto ctx = create_android_logger(TAG_DEF_LOG_TAG);
+ auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+ if (android_log_write_int32(ctx, static_cast<int32_t>(tag) < 0) ||
+ android_log_write_string8_len(ctx, Name.c_str(), Name.size()) < 0 ||
+ android_log_write_string8_len(ctx, Format.c_str(), Format.size()) < 0) {
+ return;
+ }
+
+ const char* cp = nullptr;
+ ssize_t len = android_log_write_list_buffer(ctx, &cp);
+
+ if (len <= 0 || cp == nullptr) {
+ return;
+ }
+
+ std::string buffer(cp, len);
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsgHeader;
+ * // what we provide to file
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_log_header_t header = {
+ .id = LOG_ID_EVENTS,
+ .tid = (uint16_t)gettid(),
+ .realtime.tv_sec = (uint32_t)ts.tv_sec,
+ .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+ };
+
+ uint32_t outTag = TAG_DEF_LOG_TAG;
+ outTag = get4LE((const char*)&outTag);
+
+ android_pmsg_log_header_t pmsgHeader = {
+ .magic = LOGGER_MAGIC,
+ .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) + sizeof(outTag) +
+ buffer.length()),
+ .uid = (uint16_t)AID_ROOT,
+ .pid = (uint16_t)getpid(),
+ };
+
+ struct iovec Vec[] = { { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) },
+ { (unsigned char*)&header, sizeof(header) },
+ { (unsigned char*)&outTag, sizeof(outTag) },
+ { (unsigned char*)const_cast<char*>(buffer.data()),
+ buffer.length() } };
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else if (uid != AID_ROOT) {
+ pmsgHeader.uid = (uint16_t)uid;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else {
+ for (auto& it : ut->second) {
+ pmsgHeader.uid = (uint16_t)it;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ }
+ }
+}
+
+void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode =
+ O_WRONLY | O_APPEND | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ int fd = openFile(dynamic_event_log_tags, mode, true);
+ if (fd < 0) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ close(fd);
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(dynamic_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[dynamic_event_log_tags] = size + ret.length();
+}
+
+void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode =
+ O_WRONLY | O_APPEND | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ static bool one = true;
+ int fd = openFile(debug_event_log_tags, mode, one);
+ one = fd >= 0;
+ if (!one) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ close(fd);
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(debug_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[debug_event_log_tags] = size + ret.length();
+}
+
+// How we maintain some runtime or reboot stickiness
+void LogTags::WritePersistEventLogTags(uint32_t tag, uid_t uid,
+ const char* source) {
+ // very unlikely
+ bool etc = source && !strcmp(source, system_event_log_tags);
+ if (etc) return;
+
+ bool dynamic = source && !strcmp(source, dynamic_event_log_tags);
+ bool debug = (!dynamic && source && !strcmp(source, debug_event_log_tags)) ||
+ !__android_log_is_debuggable();
+
+ WritePmsgEventLogTags(tag, uid);
+
+ size_t lastTotal = 0;
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot != tag2total.end()) lastTotal = itot->second;
+ }
+
+ if (lastTotal == 0) { // denotes first time for this one
+ if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) {
+ WriteDynamicEventLogTags(tag, uid);
+ }
+
+ if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) {
+ WriteDebugEventLogTags(tag, uid);
+ }
+ }
+
+ lastTotal = android::sizesTotal();
+ if (!lastTotal) ++lastTotal;
+
+ // record totals for next watermark.
+ android::RWLock::AutoWLock writeLock(rwlock);
+ tag2total[tag] = lastTotal;
+}
+
+// nameToTag converts a name into an event tag. If format is NULL, then we
+// are in readonly mode.
+uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
+ std::string Name = std::string(name);
+ bool write = format != nullptr;
+ bool updateUid = uid != AID_ROOT;
+ bool updateFormat = format && *format;
+ bool unique;
+ uint32_t Tag;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ Tag = nameToTag_locked(Name, format, unique);
+ if (updateUid && (Tag != emptyTag) && !unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ if ((ut != tag2uid.end()) &&
+ (ut->second.find(uid) == ut->second.end())) {
+ unique = write; // write passthrough to update uid counts
+ if (!write) Tag = emptyTag; // deny read access
+ }
+ } else {
+ unique = write && (ut != tag2uid.end());
+ }
+ }
+ }
+
+ if (Tag == emptyTag) return Tag;
+ WritePmsgEventLogTags(Tag, uid); // record references periodically
+ if (!unique) return Tag;
+
+ bool updateWrite = false;
+ bool updateTag;
+
+ // Special case of AddEventLogTags, checks per-uid counter which makes
+ // no sense there, and is also optimized somewhat to reduce write times.
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ // double check after switch from read lock to write lock for Tag
+ updateTag = tag2name.find(Tag) == tag2name.end();
+ // unlikely, either update, race inviting conflict or multiple uids
+ if (!updateTag) {
+ Tag = nameToTag_locked(Name, format, unique);
+ if (Tag == emptyTag) return Tag;
+ // is it multiple uid's setting this value
+ if (!unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ // Add it to the uid list
+ if ((ut == tag2uid.end()) ||
+ (ut->second.find(uid) != ut->second.end())) {
+ return Tag;
+ }
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ updateWrite = true;
+ } else {
+ if (ut == tag2uid.end()) return Tag;
+ // (system) adding a global one, erase the uid list
+ tag2uid.erase(ut);
+ updateWrite = true;
+ }
+ }
+ }
+
+ // Update section
+ size_t count;
+ if (updateUid) {
+ count = 0;
+ uid2count_const_iterator ci = uid2count.find(uid);
+ if (ci != uid2count.end()) {
+ count = ci->second;
+ if (count >= max_per_uid) {
+ if (!updateWrite) return emptyTag;
+ // If we are added to the per-Uid perms, leak the Tag
+ // if it already exists.
+ updateUid = false;
+ updateTag = false;
+ updateFormat = false;
+ }
+ }
+ }
+
+ // updateWrite -> trigger output on modified content, reset tag2total
+ // also sets static to dynamic entries if they are alterred,
+ // only occurs if they have a uid, and runtime adds another uid.
+ if (updateWrite) tag2total[Tag] = 0;
+
+ if (updateTag) {
+ // mark as a dynamic entry, but do not upset current total counter
+ tag2total_const_iterator itot = tag2total.find(Tag);
+ if (itot == tag2total.end()) tag2total[Tag] = 0;
+
+ if (*format) {
+ key2tag[Name + "+" + format] = Tag;
+ if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag;
+ } else {
+ key2tag[Name] = Tag;
+ }
+ tag2name[Tag] = Name;
+ }
+ if (updateFormat) tag2format[Tag] = format;
+
+ if (updateUid) {
+ tag2uid[Tag].emplace(uid);
+ uid2count[uid] = count + 1;
+ }
+ }
+
+ if (updateTag || updateFormat || updateWrite) {
+ WritePersistEventLogTags(Tag, uid);
+ }
+
+ return Tag;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid, const char* name,
+ const char* format) {
+ if (!format || !format[0]) {
+ return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name);
+ }
+ size_t len = (strlen(name) + 7) / 8;
+ static const char tabs[] = "\t\t\t";
+ if (len > strlen(tabs)) len = strlen(tabs);
+ std::string Uid;
+ if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid);
+ return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n", tag, name,
+ &tabs[len], format, Uid.c_str());
+}
+
+std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate) {
+ const char* name = tag2name[tag].c_str();
+
+ const char* format = "";
+ tag2format_const_iterator iform = tag2format.find(tag);
+ if (iform != tag2format.end()) format = iform->second.c_str();
+
+ // Access permission test, do not report dynamic entries
+ // that do not belong to us.
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ return formatEntry(tag, AID_ROOT, name, format);
+ }
+ if (uid != AID_ROOT) {
+ if (authenticate && (ut->second.find(uid) == ut->second.end())) {
+ return std::string("");
+ }
+ return formatEntry(tag, uid, name, format);
+ }
+
+ // Show all, one for each registered uid (we are group root)
+ std::string ret;
+ for (auto& it : ut->second) {
+ ret += formatEntry(tag, it, name, format);
+ }
+ return ret;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+ return formatEntry_locked(tag, uid);
+}
+
+std::string LogTags::formatGetEventTag(uid_t uid, const char* name,
+ const char* format) {
+ bool all = name && (name[0] == '*') && !name[1];
+ bool list = !name || all;
+ std::string ret;
+
+ if (!list) {
+ // switch to read entry only if format == "*"
+ if (format && (format[0] == '*') && !format[1]) format = nullptr;
+
+ // WAI: for null format, only works for a single entry, we can have
+ // multiple entries, one for each format, so we find first entry
+ // recorded, or entry with no format associated with it.
+ // We may desire to print all that match the name, but we did not
+ // add a mapping table for that and the cost is too high.
+ uint32_t tag = nameToTag(uid, name, format);
+ if (tag == emptyTag) return std::string("-1 ESRCH");
+ if (uid == AID_ROOT) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ // first uid in list so as to manufacture an accurate reference
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if ((ut != tag2uid.end()) &&
+ (ut->second.begin() != ut->second.end())) {
+ uid = *(ut->second.begin());
+ }
+ }
+ ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag));
+ if (!ret.length()) return std::string("-1 ESRCH");
+ return ret;
+ }
+
+ android::RWLock::AutoRLock readLock(rwlock);
+ if (all) {
+ // everything under the sun
+ for (const auto& it : tag2name) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ } else {
+ // set entries are dynamic
+ for (const auto& it : tag2total) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ }
+ return ret;
+}
diff --git a/logd/LogTags.h b/logd/LogTags.h
new file mode 100644
index 000000000..e4d165a7c
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_TAGS_H__
+#define _LOGD_LOG_TAGS_H__
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <utils/RWLock.h>
+
+class LogTags {
+ // This lock protects all the unordered_map accesses below. It
+ // is a reader/writer lock so that contentions are kept to a
+ // minimum since writes are rare, even administratably when
+ // reads are extended. Resist the temptation to use the writer
+ // lock to protect anything outside the following unordered_maps
+ // as that would increase the reader contentions. Use a separate
+ // mutex to protect the other entities.
+ android::RWLock rwlock;
+
+ // key is Name + "+" + Format
+ std::unordered_map<std::string, uint32_t> key2tag;
+ typedef std::unordered_map<std::string, uint32_t>::const_iterator
+ key2tag_const_iterator;
+
+ // Allows us to manage access permissions based on uid registrants
+ // Global entries are specifically erased.
+ typedef std::unordered_set<uid_t> uid_list;
+ std::unordered_map<uint32_t, uid_list> tag2uid;
+ typedef std::unordered_map<uint32_t, uid_list>::const_iterator
+ tag2uid_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2name;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator
+ tag2name_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2format;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator
+ tag2format_const_iterator;
+
+ static const size_t max_per_uid = 256; // Put a cap on the tags per uid
+ std::unordered_map<uid_t, size_t> uid2count;
+ typedef std::unordered_map<uid_t, size_t>::const_iterator
+ uid2count_const_iterator;
+
+ // Dynamic entries are assigned
+ std::unordered_map<uint32_t, size_t> tag2total;
+ typedef std::unordered_map<uint32_t, size_t>::const_iterator
+ tag2total_const_iterator;
+
+ // emplace unique tag
+ uint32_t nameToTag(uid_t uid, const char* name, const char* format);
+ // find unique or associated tag
+ uint32_t nameToTag_locked(const std::string& name, const char* format,
+ bool& unique);
+
+ // Record expected file watermarks to detect corruption.
+ std::unordered_map<std::string, size_t> file2watermark;
+ typedef std::unordered_map<std::string, size_t>::const_iterator
+ file2watermark_const_iterator;
+
+ void ReadPersistEventLogTags();
+
+ // format helpers
+ // format a single entry, does not need object data
+ static std::string formatEntry(uint32_t tag, uid_t uid, const char* name,
+ const char* format);
+ // caller locks, database lookup, authenticate against uid
+ std::string formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate = true);
+
+ bool RebuildFileEventLogTags(const char* filename, bool warn = true);
+
+ void AddEventLogTags(uint32_t tag, uid_t uid, const std::string& Name,
+ const std::string& Format, const char* source = nullptr,
+ bool warn = false);
+
+ void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
+ void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
+ // push tag details to persistent storage
+ void WritePersistEventLogTags(uint32_t tag, uid_t uid = AID_ROOT,
+ const char* source = nullptr);
+
+ static const uint32_t emptyTag = uint32_t(-1);
+
+ public:
+ static const char system_event_log_tags[];
+ static const char dynamic_event_log_tags[];
+ // Only for userdebug and eng
+ static const char debug_event_log_tags[];
+
+ LogTags();
+
+ void WritePmsgEventLogTags(uint32_t tag, uid_t uid = AID_ROOT);
+ void ReadFileEventLogTags(const char* filename, bool warn = true);
+
+ // reverse lookup from tag
+ const char* tagToName(uint32_t tag) const;
+ const char* tagToFormat(uint32_t tag) const;
+ std::string formatEntry(uint32_t tag, uid_t uid);
+ // find associated tag
+ uint32_t nameToTag(const char* name) const;
+
+ // emplace tag if necessary, provide event-log-tag formated output in string
+ std::string formatGetEventTag(uid_t uid, const char* name,
+ const char* format);
+};
+
+#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
new file mode 100644
index 000000000..208d67f49
--- /dev/null
+++ b/logd/LogTimes.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include <private/android_logger.h>
+
+#include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogReader.h"
+#include "LogTimes.h"
+
+pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
+
+LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
+ bool nonBlock, unsigned long tail, log_mask_t logMask,
+ pid_t pid, log_time start, uint64_t timeout)
+ : leadingDropped(false),
+ mReader(reader),
+ mLogMask(logMask),
+ mPid(pid),
+ mCount(0),
+ mTail(tail),
+ mIndex(0),
+ mClient(client),
+ mStart(start),
+ mNonBlock(nonBlock),
+ mEnd(log_time(android_log_clockid())) {
+ mTimeout.tv_sec = timeout / NS_PER_SEC;
+ mTimeout.tv_nsec = timeout % NS_PER_SEC;
+ memset(mLastTid, 0, sizeof(mLastTid));
+ pthread_cond_init(&threadTriggeredCondition, nullptr);
+ cleanSkip_Locked();
+}
+
+bool LogTimeEntry::startReader_Locked() {
+ pthread_attr_t attr;
+
+ if (!pthread_attr_init(&attr)) {
+ if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
+ this)) {
+ pthread_attr_destroy(&attr);
+ return true;
+ }
+ }
+ pthread_attr_destroy(&attr);
+ }
+
+ return false;
+}
+
+void* LogTimeEntry::threadStart(void* obj) {
+ prctl(PR_SET_NAME, "logd.reader.per");
+
+ LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
+
+ SocketClient* client = me->mClient;
+
+ LogBuffer& logbuf = me->mReader.logbuf();
+
+ bool privileged = FlushCommand::hasReadLogs(client);
+ bool security = FlushCommand::hasSecurityLogs(client);
+
+ me->leadingDropped = true;
+
+ wrlock();
+
+ log_time start = me->mStart;
+
+ while (!me->mRelease) {
+ if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ if (pthread_cond_timedwait(&me->threadTriggeredCondition,
+ &timesLock, &me->mTimeout) == ETIMEDOUT) {
+ me->mTimeout.tv_sec = 0;
+ me->mTimeout.tv_nsec = 0;
+ }
+ if (me->mRelease) {
+ break;
+ }
+ }
+
+ unlock();
+
+ if (me->mTail) {
+ logbuf.flushTo(client, start, nullptr, privileged, security,
+ FilterFirstPass, me);
+ me->leadingDropped = true;
+ }
+ start = logbuf.flushTo(client, start, me->mLastTid, privileged,
+ security, FilterSecondPass, me);
+
+ wrlock();
+
+ if (start == LogBufferElement::FLUSH_ERROR) {
+ break;
+ }
+
+ me->mStart = start + log_time(0, 1);
+
+ if (me->mNonBlock || me->mRelease) {
+ break;
+ }
+
+ me->cleanSkip_Locked();
+
+ if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
+ pthread_cond_wait(&me->threadTriggeredCondition, &timesLock);
+ }
+ }
+
+ LogReader& reader = me->mReader;
+ reader.release(client);
+
+ client->decRef();
+
+ LastLogTimes& times = reader.logbuf().mTimes;
+ auto it =
+ std::find_if(times.begin(), times.end(),
+ [&me](const auto& other) { return other.get() == me; });
+
+ if (it != times.end()) {
+ times.erase(it);
+ }
+
+ unlock();
+
+ return nullptr;
+}
+
+// A first pass to count the number of elements
+int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
+ LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
+
+ LogTimeEntry::wrlock();
+
+ if (me->leadingDropped) {
+ if (element->getDropped()) {
+ LogTimeEntry::unlock();
+ return false;
+ }
+ me->leadingDropped = false;
+ }
+
+ if (me->mCount == 0) {
+ me->mStart = element->getRealTime();
+ }
+
+ if ((!me->mPid || (me->mPid == element->getPid())) &&
+ (me->isWatching(element->getLogId()))) {
+ ++me->mCount;
+ }
+
+ LogTimeEntry::unlock();
+
+ return false;
+}
+
+// A second pass to send the selected elements
+int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
+ LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
+
+ LogTimeEntry::wrlock();
+
+ me->mStart = element->getRealTime();
+
+ if (me->skipAhead[element->getLogId()]) {
+ me->skipAhead[element->getLogId()]--;
+ goto skip;
+ }
+
+ if (me->leadingDropped) {
+ if (element->getDropped()) {
+ goto skip;
+ }
+ me->leadingDropped = false;
+ }
+
+ // Truncate to close race between first and second pass
+ if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
+ goto stop;
+ }
+
+ if (!me->isWatching(element->getLogId())) {
+ goto skip;
+ }
+
+ if (me->mPid && (me->mPid != element->getPid())) {
+ goto skip;
+ }
+
+ if (me->mRelease) {
+ goto stop;
+ }
+
+ if (!me->mTail) {
+ goto ok;
+ }
+
+ ++me->mIndex;
+
+ if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
+ goto skip;
+ }
+
+ if (!me->mNonBlock) {
+ me->mTail = 0;
+ }
+
+ok:
+ if (!me->skipAhead[element->getLogId()]) {
+ LogTimeEntry::unlock();
+ return true;
+ }
+// FALLTHRU
+
+skip:
+ LogTimeEntry::unlock();
+ return false;
+
+stop:
+ LogTimeEntry::unlock();
+ return -1;
+}
+
+void LogTimeEntry::cleanSkip_Locked(void) {
+ memset(skipAhead, 0, sizeof(skipAhead));
+}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
new file mode 100644
index 000000000..9f6f20335
--- /dev/null
+++ b/logd/LogTimes.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_TIMES_H__
+#define _LOGD_LOG_TIMES_H__
+
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <list>
+#include <memory>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+typedef unsigned int log_mask_t;
+
+class LogReader;
+class LogBufferElement;
+
+class LogTimeEntry {
+ static pthread_mutex_t timesLock;
+ bool mRelease = false;
+ bool leadingDropped;
+ pthread_cond_t threadTriggeredCondition;
+ pthread_t mThread;
+ LogReader& mReader;
+ static void* threadStart(void* me);
+ const log_mask_t mLogMask;
+ const pid_t mPid;
+ unsigned int skipAhead[LOG_ID_MAX];
+ pid_t mLastTid[LOG_ID_MAX];
+ unsigned long mCount;
+ unsigned long mTail;
+ unsigned long mIndex;
+
+ public:
+ LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
+ unsigned long tail, log_mask_t logMask, pid_t pid,
+ log_time start, uint64_t timeout);
+
+ SocketClient* mClient;
+ log_time mStart;
+ struct timespec mTimeout;
+ const bool mNonBlock;
+ const log_time mEnd; // only relevant if mNonBlock
+
+ // Protect List manipulations
+ static void wrlock(void) {
+ pthread_mutex_lock(&timesLock);
+ }
+ static void rdlock(void) {
+ pthread_mutex_lock(&timesLock);
+ }
+ static void unlock(void) {
+ pthread_mutex_unlock(&timesLock);
+ }
+
+ bool startReader_Locked();
+
+ void triggerReader_Locked(void) {
+ pthread_cond_signal(&threadTriggeredCondition);
+ }
+
+ void triggerSkip_Locked(log_id_t id, unsigned int skip) {
+ skipAhead[id] = skip;
+ }
+ void cleanSkip_Locked(void);
+
+ void release_Locked(void) {
+ // gracefully shut down the socket.
+ shutdown(mClient->getSocket(), SHUT_RDWR);
+ mRelease = true;
+ pthread_cond_signal(&threadTriggeredCondition);
+ }
+
+ bool isWatching(log_id_t id) const {
+ return mLogMask & (1 << id);
+ }
+ bool isWatchingMultiple(log_mask_t logMask) const {
+ return mLogMask & logMask;
+ }
+ // flushTo filter callbacks
+ static int FilterFirstPass(const LogBufferElement* element, void* me);
+ static int FilterSecondPass(const LogBufferElement* element, void* me);
+};
+
+typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
+
+#endif // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
new file mode 100644
index 000000000..fa9f39895
--- /dev/null
+++ b/logd/LogUtils.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012-2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_UTILS_H__
+#define _LOGD_LOG_UTILS_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <private/android_logger.h>
+#include <sysutils/SocketClient.h>
+#include <utils/FastStrcmp.h>
+
+// Hijack this header as a common include file used by most all sources
+// to report some utilities defined here and there.
+
+namespace android {
+
+// Furnished in main.cpp. Caller must own and free returned value
+char* uidToName(uid_t uid);
+void prdebug(const char* fmt, ...) __printflike(1, 2);
+
+// Furnished in LogStatistics.cpp.
+size_t sizesTotal();
+// Caller must own and free returned value
+char* pidToName(pid_t pid);
+char* tidToName(pid_t tid);
+
+// Furnished in LogTags.cpp. Thread safe.
+const char* tagToName(uint32_t tag);
+void ReReadEventLogTags();
+
+// Furnished by LogKlog.cpp
+char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
+
+// needle should reference a string longer than 1 character
+static inline const char* strnstr(const char* s, ssize_t len,
+ const char* needle) {
+ if (len <= 0) return nullptr;
+
+ const char c = *needle++;
+ const size_t needleLen = strlen(needle);
+ do {
+ do {
+ if (len <= (ssize_t)needleLen) return nullptr;
+ --len;
+ } while (*s++ != c);
+ } while (fastcmp<memcmp>(s, needle, needleLen));
+ s--;
+ return s;
+}
+}
+
+// Furnished in LogCommand.cpp
+bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
+bool clientHasLogCredentials(SocketClient* cli);
+
+static inline bool worstUidEnabledForLogid(log_id_t id) {
+ return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
+ (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
+}
+
+#endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
new file mode 100644
index 000000000..9d762dca4
--- /dev/null
+++ b/logd/LogWhiteBlackList.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+
+#include "LogWhiteBlackList.h"
+
+// White and Black list
+
+Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
+}
+
+int Prune::cmp(uid_t uid, pid_t pid) const {
+ if ((mUid == uid_all) || (mUid == uid)) {
+ if (mPid == pid_all) {
+ return 0;
+ }
+ return pid - mPid;
+ }
+ return uid - mUid;
+}
+
+std::string Prune::format() {
+ if (mUid != uid_all) {
+ if (mPid != pid_all) {
+ return android::base::StringPrintf("%u/%u", mUid, mPid);
+ }
+ return android::base::StringPrintf("%u", mUid);
+ }
+ if (mPid != pid_all) {
+ return android::base::StringPrintf("/%u", mPid);
+ }
+ // NB: mPid == pid_all can not happen if mUid == uid_all
+ return std::string("/");
+}
+
+PruneList::PruneList() {
+ init(nullptr);
+}
+
+PruneList::~PruneList() {
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end();) {
+ it = mNice.erase(it);
+ }
+ for (it = mNaughty.begin(); it != mNaughty.end();) {
+ it = mNaughty.erase(it);
+ }
+}
+
+int PruneList::init(const char* str) {
+ mWorstUidEnabled = true;
+ mWorstPidOfSystemEnabled = true;
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end();) {
+ it = mNice.erase(it);
+ }
+ for (it = mNaughty.begin(); it != mNaughty.end();) {
+ it = mNaughty.erase(it);
+ }
+
+ static const char _default[] = "default";
+ // default here means take ro.logd.filter, persist.logd.filter then
+ // internal default in that order.
+ if (str && !strcmp(str, _default)) {
+ str = nullptr;
+ }
+ static const char _disable[] = "disable";
+ if (str && !strcmp(str, _disable)) {
+ str = "";
+ }
+
+ std::string filter;
+
+ if (str) {
+ filter = str;
+ } else {
+ char property[PROPERTY_VALUE_MAX];
+ property_get("ro.logd.filter", property, _default);
+ filter = property;
+ property_get("persist.logd.filter", property, filter.c_str());
+ // default here means take ro.logd.filter
+ if (strcmp(property, _default)) {
+ filter = property;
+ }
+ }
+
+ // default here means take internal default.
+ if (filter == _default) {
+ // See README.property for description of filter format
+ filter = "~! ~1000/!";
+ }
+ if (filter == _disable) {
+ filter = "";
+ }
+
+ mWorstUidEnabled = false;
+ mWorstPidOfSystemEnabled = false;
+
+ for (str = filter.c_str(); *str; ++str) {
+ if (isspace(*str)) {
+ continue;
+ }
+
+ PruneCollection* list;
+ if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
+ ++str;
+ // special case, translates to worst UID at priority in blacklist
+ if (*str == '!') {
+ mWorstUidEnabled = true;
+ ++str;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ return 1;
+ }
+ continue;
+ }
+ // special case, translated to worst PID of System at priority
+ static const char worstPid[] = "1000/!";
+ if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
+ mWorstPidOfSystemEnabled = true;
+ str += sizeof(worstPid) - 1;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ return 1;
+ }
+ continue;
+ }
+ if (!*str) {
+ return 1;
+ }
+ list = &mNaughty;
+ } else {
+ list = &mNice;
+ }
+
+ uid_t uid = Prune::uid_all;
+ if (isdigit(*str)) {
+ uid = 0;
+ do {
+ uid = uid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+
+ pid_t pid = Prune::pid_all;
+ if (*str == '/') {
+ ++str;
+ if (isdigit(*str)) {
+ pid = 0;
+ do {
+ pid = pid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+ }
+
+ if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
+ return 1;
+ }
+
+ if (*str && !isspace(*str)) {
+ return 1;
+ }
+
+ // insert sequentially into list
+ PruneCollection::iterator it = list->begin();
+ while (it != list->end()) {
+ Prune& p = *it;
+ int m = uid - p.mUid;
+ if (m == 0) {
+ if (p.mPid == p.pid_all) {
+ break;
+ }
+ if ((pid == p.pid_all) && (p.mPid != p.pid_all)) {
+ it = list->erase(it);
+ continue;
+ }
+ m = pid - p.mPid;
+ }
+ if (m <= 0) {
+ if (m < 0) {
+ list->insert(it, Prune(uid, pid));
+ }
+ break;
+ }
+ ++it;
+ }
+ if (it == list->end()) {
+ list->push_back(Prune(uid, pid));
+ }
+ if (!*str) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+std::string PruneList::format() {
+ static const char nice_format[] = " %s";
+ const char* fmt = nice_format + 1;
+
+ std::string string;
+
+ if (mWorstUidEnabled) {
+ string = "~!";
+ fmt = nice_format;
+ if (mWorstPidOfSystemEnabled) {
+ string += " ~1000/!";
+ }
+ }
+
+ PruneCollection::iterator it;
+
+ for (it = mNice.begin(); it != mNice.end(); ++it) {
+ string += android::base::StringPrintf(fmt, (*it).format().c_str());
+ fmt = nice_format;
+ }
+
+ static const char naughty_format[] = " ~%s";
+ fmt = naughty_format + (*fmt != ' ');
+ for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+ string += android::base::StringPrintf(fmt, (*it).format().c_str());
+ fmt = naughty_format;
+ }
+
+ return string;
+}
+
+// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
+// If there is scaling issues, resort to a better algorithm than linear
+// based on these assumptions.
+
+bool PruneList::naughty(LogBufferElement* element) {
+ PruneCollection::iterator it;
+ for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+ if (!(*it).cmp(element)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PruneList::nice(LogBufferElement* element) {
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end(); ++it) {
+ if (!(*it).cmp(element)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
new file mode 100644
index 000000000..6e9893b6d
--- /dev/null
+++ b/logd/LogWhiteBlackList.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
+#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+
+#include <sys/types.h>
+
+#include <string.h>
+#include <list>
+
+#include "LogBufferElement.h"
+
+// White and Blacklist
+
+class Prune {
+ friend class PruneList;
+
+ const uid_t mUid;
+ const pid_t mPid;
+ int cmp(uid_t uid, pid_t pid) const;
+
+ public:
+ static const uid_t uid_all = (uid_t)-1;
+ static const pid_t pid_all = (pid_t)-1;
+
+ Prune(uid_t uid, pid_t pid);
+
+ uid_t getUid() const {
+ return mUid;
+ }
+ pid_t getPid() const {
+ return mPid;
+ }
+
+ int cmp(LogBufferElement* e) const {
+ return cmp(e->getUid(), e->getPid());
+ }
+
+ std::string format();
+};
+
+typedef std::list<Prune> PruneCollection;
+
+class PruneList {
+ PruneCollection mNaughty;
+ PruneCollection mNice;
+ bool mWorstUidEnabled;
+ bool mWorstPidOfSystemEnabled;
+
+ public:
+ PruneList();
+ ~PruneList();
+
+ int init(const char* str);
+
+ bool naughty(LogBufferElement* element);
+ bool naughty(void) {
+ return !mNaughty.empty();
+ }
+ bool nice(LogBufferElement* element);
+ bool nice(void) {
+ return !mNice.empty();
+ }
+ bool worstUidEnabled() const {
+ return mWorstUidEnabled;
+ }
+ bool worstPidOfSystemEnabled() const {
+ return mWorstPidOfSystemEnabled;
+ }
+
+ std::string format();
+};
+
+#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/OWNERS b/logd/OWNERS
new file mode 100644
index 000000000..2394e32e9
--- /dev/null
+++ b/logd/OWNERS
@@ -0,0 +1,2 @@
+cferris@google.com
+tomcherry@google.com
diff --git a/logd/README.auditd b/logd/README.auditd
new file mode 100644
index 000000000..3f614a341
--- /dev/null
+++ b/logd/README.auditd
@@ -0,0 +1,17 @@
+Auditd Daemon
+
+The audit daemon is a simplified version of its desktop
+counterpart designed to gather the audit logs from the
+audit kernel subsystem. The audit subsystem of the kernel
+includes Linux Security Modules (LSM) messages as well.
+
+To enable the audit subsystem, you must add this to your
+kernel config:
+CONFIG_AUDIT=y
+
+To enable a LSM, you must consult that LSM's documentation, the
+example below is for SELinux:
+CONFIG_SECURITY_SELINUX=y
+
+This does not include possible dependencies that may need to be
+satisfied for that particular LSM.
diff --git a/logd/README.property b/logd/README.property
new file mode 100644
index 000000000..1b7e1652c
--- /dev/null
+++ b/logd/README.property
@@ -0,0 +1,76 @@
+The properties that logd and friends react to are:
+
+name type default description
+ro.logd.auditd bool true Enable selinux audit daemon
+ro.logd.auditd.dmesg bool true selinux audit messages sent to dmesg.
+ro.logd.auditd.main bool true selinux audit messages sent to main.
+ro.logd.auditd.events bool true selinux audit messages sent to events.
+persist.logd.security bool false Enable security buffer.
+ro.organization_owned bool false Override persist.logd.security to false
+ro.logd.kernel bool+ svelte+ Enable klogd daemon
+ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
+ro.debuggable number if not "1", logd.statistics &
+ ro.logd.kernel default false.
+logd.logpersistd.enable bool auto Safe to start logpersist daemon service
+logd.logpersistd string persist Enable logpersist daemon, "logcatd"
+ turns on logcat -f in logd context.
+ Responds to logcatd, clear and stop.
+logd.logpersistd.buffer persist logpersistd buffers to collect
+logd.logpersistd.size persist logpersistd size in MB
+logd.logpersistd.rotate_kbytes persist logpersistd outout file size in KB.
+persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
+ turns on logcat -f in logd context.
+persist.logd.logpersistd.buffer all logpersistd buffers to collect
+persist.logd.logpersistd.size 256 logpersistd size in MB
+persist.logd.logpersistd.count 256 sets max number of rotated logs to <count>.
+persist.logd.logpersistd.rotate_kbytes 1024 logpersistd output file size in KB
+persist.logd.size number ro Global default size of the buffer for
+ all log ids at initial startup, at
+ runtime use: logcat -b all -G <value>
+ro.logd.size number svelte default for persist.logd.size. Larger
+ platform default sizes than 256KB are
+ known to not scale well under log spam
+ pressure. Address the spam first,
+ resist increasing the log buffer.
+persist.logd.size.<buffer> number ro Size of the buffer for <buffer> log
+ro.logd.size.<buffer> number svelte default for persist.logd.size.<buffer>
+ro.config.low_ram bool false if true, logd.statistics,
+ ro.logd.kernel default false,
+ logd.size 64K instead of 256K.
+persist.logd.filter string Pruning filter to optimize content.
+ At runtime use: logcat -P "<string>"
+ro.logd.filter string "~! ~1000/!" default for persist.logd.filter.
+ This default means to prune the
+ oldest entries of chattiest UID, and
+ the chattiest PID of system
+ (1000, or AID_SYSTEM).
+persist.logd.timestamp string ro The recording timestamp source.
+ "m[onotonic]" is the only supported
+ key character, otherwise realtime.
+ro.logd.timestamp string realtime default for persist.logd.timestamp
+log.tag string persist The global logging level, VERBOSE,
+ DEBUG, INFO, WARN, ERROR, ASSERT or
+ SILENT. Only the first character is
+ the key character.
+persist.log.tag string build default for log.tag
+log.tag.<tag> string persist The <tag> specific logging level.
+persist.log.tag.<tag> string build default for log.tag.<tag>
+
+NB:
+- auto - managed by /init
+- bool+ - "true", "false" and comma separated list of "eng" (forced false if
+ ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
+ true).
+- svelte - see ro.config.low_ram for details.
+- svelte+ - see ro.config.low_ram and ro.debuggable for details.
+- ro - <base property> temporary override, ro.<base property> platform default.
+- persist - <base property> override, persist.<base property> platform default.
+- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
+- number - support multipliers (K or M) for convenience. Range is limited
+ to between 64K and 256M for log buffer sizes. Individual log buffer ids
+ such as main, system, ... override global default.
+- Pruning filter is of form of a space-separated list of [~][UID][/PID]
+ references, where '~' prefix means to blacklist otherwise whitelist. For
+ blacklisting, UID or PID may be a '!' to instead reference the chattiest
+ client, with the restriction that the PID must be in the UID group 1000
+ (system or AID_SYSTEM).
diff --git a/logd/auditctl.cpp b/logd/auditctl.cpp
new file mode 100644
index 000000000..98bb02dbe
--- /dev/null
+++ b/logd/auditctl.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/parseint.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libaudit.h"
+
+static void usage(const char* cmdline) {
+ fprintf(stderr, "Usage: %s [-r rate]\n", cmdline);
+}
+
+static void do_update_rate(uint32_t rate) {
+ int fd = audit_open();
+ if (fd == -1) {
+ error(EXIT_FAILURE, errno, "Unable to open audit socket");
+ }
+ int result = audit_rate_limit(fd, rate);
+ close(fd);
+ if (result < 0) {
+ fprintf(stderr, "Can't update audit rate limit: %d\n", result);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ uint32_t rate = 0;
+ bool update_rate = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "r:")) != -1) {
+ switch (opt) {
+ case 'r':
+ if (!android::base::ParseUint<uint32_t>(optarg, &rate)) {
+ error(EXIT_FAILURE, errno, "Invalid Rate");
+ }
+ update_rate = true;
+ break;
+ default: /* '?' */
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // In the future, we may add other options to auditctl
+ // so this if statement will expand.
+ // if (!update_rate && !update_backlog && !update_whatever) ...
+ if (!update_rate) {
+ fprintf(stderr, "Nothing to do\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (update_rate) {
+ do_update_rate(rate);
+ }
+
+ return 0;
+}
diff --git a/logd/event.logtags b/logd/event.logtags
new file mode 100644
index 000000000..fa13a628d
--- /dev/null
+++ b/logd/event.logtags
@@ -0,0 +1,39 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace. Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+# (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# s: Number of seconds (monotonic time)
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+1003 auditd (avc|3)
+1004 chatty (dropped|3)
+1005 tag_def (tag|1),(name|3),(format|3)
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
new file mode 100644
index 000000000..299242db6
--- /dev/null
+++ b/logd/fuzz/Android.bp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+cc_fuzz {
+ name: "log_buffer_log_fuzzer",
+ srcs: [
+ "log_buffer_log_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "libselinux",
+ "liblog",
+ "liblogd",
+ "libcutils",
+ ],
+ cflags: ["-Werror"],
+}
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
new file mode 100644
index 000000000..4d1589b50
--- /dev/null
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <string>
+
+#include "../LogBuffer.h"
+#include "../LogTimes.h"
+
+// We don't want to waste a lot of entropy on messages
+#define MAX_MSG_LENGTH 5
+
+// Tag IDs usually start at 1000, we only want to try 1000 through 1009
+#define MIN_TAG_ID 1000
+#define TAG_MOD 10
+
+namespace android {
+struct LogInput {
+ public:
+ log_id_t log_id;
+ log_time realtime;
+ uid_t uid;
+ pid_t pid;
+ pid_t tid;
+ unsigned int log_mask;
+};
+
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+ const uint8_t* data = *pdata;
+ const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
+ data += sizeof(LogInput);
+ *data_left -= sizeof(LogInput);
+
+ uint32_t tag = MIN_TAG_ID + data[0] % TAG_MOD;
+ uint8_t msg_length = data[1] % MAX_MSG_LENGTH;
+ if (msg_length < 2) {
+ // Not enough data for message
+ return 0;
+ }
+
+ data += 2 * sizeof(uint8_t);
+ *data_left -= 2 * sizeof(uint8_t);
+
+ if (*data_left < msg_length) {
+ // Not enough data for tag and message
+ *pdata = data;
+ return 0;
+ }
+
+ // We need nullterm'd strings
+ char msg[sizeof(uint32_t) + MAX_MSG_LENGTH + sizeof(char)];
+ char* msg_only = msg + sizeof(uint32_t);
+ memcpy(msg, &tag, sizeof(uint32_t));
+ memcpy(msg_only, data, msg_length);
+ msg_only[msg_length] = '\0';
+ data += msg_length;
+ *data_left -= msg_length;
+
+ // Other elements not in enum.
+ log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
+ log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
+ sizeof(uint32_t) + msg_length + 1);
+ log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+ *pdata = data;
+ return 1;
+}
+
+// Because system/core/logd/main.cpp redefines these.
+void prdebug(char const* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+char* uidToName(uid_t) {
+ return strdup("fake");
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // We want a random tag length and a random remaining message length
+ if (data == nullptr || size < sizeof(LogInput) + 2 * sizeof(uint8_t)) {
+ return 0;
+ }
+
+ LastLogTimes times;
+ LogBuffer log_buffer(&times);
+ size_t data_left = size;
+ const uint8_t** pdata = &data;
+
+ log_buffer.enableStatistics();
+ log_buffer.initPrune(nullptr);
+ // We want to get pruning code to get called.
+ log_id_for_each(i) { log_buffer.setSize(i, 10000); }
+
+ while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
+ if (!write_log_messages(pdata, &data_left, &log_buffer)) {
+ return 0;
+ }
+ }
+
+ log_id_for_each(i) { log_buffer.clear(i); }
+ return 0;
+}
+} // namespace android
diff --git a/logd/libaudit.c b/logd/libaudit.c
new file mode 100644
index 000000000..f452c71ab
--- /dev/null
+++ b/logd/libaudit.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libaudit.h"
+
+/**
+ * Waits for an ack from the kernel
+ * @param fd
+ * The netlink socket fd
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+static int get_ack(int fd) {
+ int rc;
+ struct audit_message rep;
+
+ /* Sanity check, this is an internal interface this shouldn't happen */
+ if (fd < 0) {
+ return -EINVAL;
+ }
+
+ rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+ if (rc < 0) {
+ return rc;
+ }
+
+ if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
+ audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
+ rc = ((struct nlmsgerr*)rep.data)->error;
+ if (rc) {
+ return -rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ *
+ * @param fd
+ * The netlink socket fd
+ * @param type
+ * The type of netlink message
+ * @param data
+ * The data to send
+ * @param size
+ * The length of the data in bytes
+ * @return
+ * This function returns a positive sequence number on success, else -errno.
+ */
+static int audit_send(int fd, int type, const void* data, size_t size) {
+ int rc;
+ static int16_t sequence = 0;
+ struct audit_message req;
+ struct sockaddr_nl addr;
+
+ memset(&req, 0, sizeof(req));
+ memset(&addr, 0, sizeof(addr));
+
+ /* We always send netlink messaged */
+ addr.nl_family = AF_NETLINK;
+
+ /* Set up the netlink headers */
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_len = NLMSG_SPACE(size);
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+ /*
+ * Check for a valid fd, even though sendto would catch this, its easier
+ * to always blindly increment the sequence number
+ */
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure the message is not too big */
+ if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
+ return -EINVAL;
+ }
+
+ /* Only memcpy in the data if it was specified */
+ if (size && data) {
+ memcpy(NLMSG_DATA(&req.nlh), data, size);
+ }
+
+ /*
+ * Only increment the sequence number on a guarantee
+ * you will send it to the kernel.
+ *
+ * Also, the sequence is defined as a u32 in the kernel
+ * struct. Using an int here might not work on 32/64 bit splits. A
+ * signed 64 bit value can overflow a u32..but a u32
+ * might not fit in the response, so we need to use s32.
+ * Which is still kind of hackish since int could be 16 bits
+ * in size. The only safe type to use here is a signed 16
+ * bit value.
+ */
+ req.nlh.nlmsg_seq = ++sequence;
+
+ /* While failing and its due to interrupts */
+
+ rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
+ (struct sockaddr*)&addr, sizeof(addr)));
+
+ /* Not all the bytes were sent */
+ if (rc < 0) {
+ rc = -errno;
+ goto out;
+ } else if ((uint32_t)rc != req.nlh.nlmsg_len) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ /* We sent all the bytes, get the ack */
+ rc = get_ack(fd);
+
+ /* If the ack failed, return the error, else return the sequence number */
+ rc = (rc == 0) ? (int)sequence : rc;
+
+out:
+ /* Don't let sequence roll to negative */
+ if (sequence < 0) {
+ sequence = 0;
+ }
+
+ return rc;
+}
+
+int audit_setup(int fd, pid_t pid) {
+ int rc;
+ struct audit_message rep;
+ struct audit_status status;
+
+ memset(&status, 0, sizeof(status));
+
+ /*
+ * In order to set the auditd PID we send an audit message over the netlink
+ * socket with the pid field of the status struct set to our current pid,
+ * and the the mask set to AUDIT_STATUS_PID
+ */
+ status.pid = pid;
+ status.mask = AUDIT_STATUS_PID;
+
+ /* Let the kernel know this pid will be registering for audit events */
+ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+ if (rc < 0) {
+ return rc;
+ }
+
+ /*
+ * In a request where we need to wait for a response, wait for the message
+ * and discard it. This message confirms and sync's us with the kernel.
+ * This daemon is now registered as the audit logger.
+ *
+ * TODO
+ * If the daemon dies and restarts the message didn't come back,
+ * so I went to non-blocking and it seemed to fix the bug.
+ * Need to investigate further.
+ */
+ audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+
+ return 0;
+}
+
+int audit_open() {
+ return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
+}
+
+int audit_rate_limit(int fd, uint32_t limit) {
+ struct audit_status status;
+ memset(&status, 0, sizeof(status));
+ status.mask = AUDIT_STATUS_RATE_LIMIT;
+ status.rate_limit = limit; /* audit entries per second */
+ return audit_send(fd, AUDIT_SET, &status, sizeof(status));
+}
+
+int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
+ ssize_t len;
+ int flags;
+ int rc = 0;
+
+ struct sockaddr_nl nladdr;
+ socklen_t nladdrlen = sizeof(nladdr);
+
+ if (fd < 0) {
+ return -EBADF;
+ }
+
+ /* Set up the flags for recv from */
+ flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+ flags |= peek;
+
+ /*
+ * Get the data from the netlink socket but on error we need to be carefull,
+ * the interface shows that EINTR can never be returned, other errors,
+ * however, can be returned.
+ */
+ len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
+ (struct sockaddr*)&nladdr, &nladdrlen));
+
+ /*
+ * EAGAIN should be re-tried until success or another error manifests.
+ */
+ if (len < 0) {
+ rc = -errno;
+ if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+ /* If request is non blocking and errno is EAGAIN, just return 0 */
+ return 0;
+ }
+ return rc;
+ }
+
+ if (nladdrlen != sizeof(nladdr)) {
+ return -EPROTO;
+ }
+
+ /* Make sure the netlink message was not spoof'd */
+ if (nladdr.nl_pid) {
+ return -EINVAL;
+ }
+
+ /* Check if the reply from the kernel was ok */
+ if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
+ rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+ }
+
+ return rc;
+}
+
+void audit_close(int fd) {
+ close(fd);
+}
diff --git a/logd/libaudit.h b/logd/libaudit.h
new file mode 100644
index 000000000..b4a92a8a3
--- /dev/null
+++ b/logd/libaudit.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ */
+
+#ifndef _LIBAUDIT_H_
+#define _LIBAUDIT_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <linux/audit.h>
+#include <linux/netlink.h>
+
+__BEGIN_DECLS
+
+#define MAX_AUDIT_MESSAGE_LENGTH 8970
+
+typedef enum { GET_REPLY_BLOCKING = 0, GET_REPLY_NONBLOCKING } reply_t;
+
+/* type == AUDIT_SIGNAL_INFO */
+struct audit_sig_info {
+ uid_t uid;
+ pid_t pid;
+ char ctx[0];
+};
+
+struct audit_message {
+ struct nlmsghdr nlh;
+ char data[MAX_AUDIT_MESSAGE_LENGTH];
+};
+
+/**
+ * Opens a connection to the Audit netlink socket
+ * @return
+ * A valid fd on success or < 0 on error with errno set.
+ * Returns the same errors as man 2 socket.
+ */
+extern int audit_open(void);
+
+/**
+ * Closes the fd returned from audit_open()
+ * @param fd
+ * The fd to close
+ */
+extern void audit_close(int fd);
+
+/**
+ *
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rep
+ * The response struct to store the response in.
+ * @param block
+ * Whether or not to block on IO
+ * @param peek
+ * Whether or not we are to remove the message from
+ * the queue when we do a read on the netlink socket.
+ * @return
+ * This function returns 0 on success, else -errno.
+ */
+extern int audit_get_reply(int fd, struct audit_message* rep, reply_t block,
+ int peek);
+
+/**
+ * Sets a pid to receive audit netlink events from the kernel
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param pid
+ * The pid whom to set as the receiver of audit messages
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+extern int audit_setup(int fd, pid_t pid);
+
+/**
+ * Throttle kernel messages at the provided rate
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rate
+ * The rate, in messages per second, above which the kernel
+ * should drop audit messages.
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+extern int audit_rate_limit(int fd, uint32_t limit);
+
+__END_DECLS
+
+#endif
diff --git a/logd/logd.rc b/logd/logd.rc
new file mode 100644
index 000000000..530f34232
--- /dev/null
+++ b/logd/logd.rc
@@ -0,0 +1,35 @@
+service logd /system/bin/logd
+ socket logd stream 0666 logd logd
+ socket logdr seqpacket 0666 logd logd
+ socket logdw dgram+passcred 0222 logd logd
+ file /proc/kmsg r
+ file /dev/kmsg w
+ user logd
+ group logd system package_info readproc
+ capabilities SYSLOG AUDIT_CONTROL
+ priority 10
+ writepid /dev/cpuset/system-background/tasks
+
+service logd-reinit /system/bin/logd --reinit
+ oneshot
+ disabled
+ user logd
+ group logd
+ writepid /dev/cpuset/system-background/tasks
+
+# Limit SELinux denial generation to 5/second
+service logd-auditctl /system/bin/auditctl -r 5
+ oneshot
+ disabled
+ user logd
+ group logd
+ capabilities AUDIT_CONTROL
+
+on fs
+ write /dev/event-log-tags "# content owned by logd
+"
+ chown logd logd /dev/event-log-tags
+ chmod 0644 /dev/event-log-tags
+
+on property:sys.boot_completed=1
+ start logd-auditctl
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
new file mode 100644
index 000000000..248a78caf
--- /dev/null
+++ b/logd/logtagd.rc
@@ -0,0 +1,9 @@
+#
+# logtagd event log tag service (debug only)
+#
+on post-fs-data
+ mkdir /data/misc/logd 0750 logd log
+ write /data/misc/logd/event-log-tags ""
+ chown logd log /data/misc/logd/event-log-tags
+ chmod 0600 /data/misc/logd/event-log-tags
+ restorecon /data/misc/logd/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
new file mode 100644
index 000000000..acf9a071f
--- /dev/null
+++ b/logd/main.cpp
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2012-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/capability.h>
+#include <poll.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/macros.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <log/event_tag_map.h>
+#include <packagelistparser/packagelistparser.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
+#include <utils/threads.h>
+
+#include "CommandListener.h"
+#include "LogAudit.h"
+#include "LogBuffer.h"
+#include "LogKlog.h"
+#include "LogListener.h"
+#include "LogUtils.h"
+
+#define KMSG_PRIORITY(PRI) \
+ '<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
+ '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
+
+// The service is designed to be run by init, it does not respond well to starting up manually. Init
+// has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec(). This
+// allows debuggers, etc to be attached to logd at the very beginning, while still having init
+// handle the user, groups, capabilities, files, etc setup.
+static int DropPrivs(bool klogd, bool auditd) {
+ if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+ android::prdebug("failed to set background scheduling policy");
+ return -1;
+ }
+
+ sched_param param = {};
+ if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
+ android::prdebug("failed to set batch scheduler");
+ return -1;
+ }
+
+ if (!__android_logger_property_get_bool("ro.debuggable",
+ BOOL_DEFAULT_FALSE) &&
+ prctl(PR_SET_DUMPABLE, 0) == -1) {
+ android::prdebug("failed to clear PR_SET_DUMPABLE");
+ return -1;
+ }
+
+ std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
+ if (cap_clear(caps.get()) < 0) {
+ android::prdebug("cap_clear() failed");
+ return -1;
+ }
+ if (klogd) {
+ cap_value_t cap_syslog = CAP_SYSLOG;
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, &cap_syslog, CAP_SET) < 0 ||
+ cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, &cap_syslog, CAP_SET) < 0) {
+ android::prdebug("Failed to set CAP_SYSLOG");
+ return -1;
+ }
+ }
+ if (auditd) {
+ cap_value_t cap_audit_control = CAP_AUDIT_CONTROL;
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, &cap_audit_control, CAP_SET) < 0 ||
+ cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, &cap_audit_control, CAP_SET) < 0) {
+ android::prdebug("Failed to set CAP_AUDIT_CONTROL");
+ return -1;
+ }
+ }
+ if (cap_set_proc(caps.get()) < 0) {
+ android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+// Property helper
+static bool check_flag(const char* prop, const char* flag) {
+ const char* cp = strcasestr(prop, flag);
+ if (!cp) {
+ return false;
+ }
+ // We only will document comma (,)
+ static const char sep[] = ",:;|+ \t\f";
+ if ((cp != prop) && !strchr(sep, cp[-1])) {
+ return false;
+ }
+ cp += strlen(flag);
+ return !*cp || !!strchr(sep, *cp);
+}
+
+static int fdDmesg = -1;
+void android::prdebug(const char* fmt, ...) {
+ if (fdDmesg < 0) {
+ return;
+ }
+
+ static const char message[] = {
+ KMSG_PRIORITY(LOG_DEBUG), 'l', 'o', 'g', 'd', ':', ' '
+ };
+ char buffer[256];
+ memcpy(buffer, message, sizeof(message));
+
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(buffer + sizeof(message),
+ sizeof(buffer) - sizeof(message), fmt, ap);
+ va_end(ap);
+ if (n > 0) {
+ buffer[sizeof(buffer) - 1] = '\0';
+ if (!strchr(buffer, '\n')) {
+ buffer[sizeof(buffer) - 2] = '\0';
+ strlcat(buffer, "\n", sizeof(buffer));
+ }
+ write(fdDmesg, buffer, strlen(buffer));
+ }
+}
+
+static sem_t reinit;
+static bool reinit_running = false;
+static LogBuffer* logBuf = nullptr;
+
+static void* reinit_thread_start(void* /*obj*/) {
+ prctl(PR_SET_NAME, "logd.daemon");
+
+ while (reinit_running && !sem_wait(&reinit) && reinit_running) {
+ if (fdDmesg >= 0) {
+ static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
+ 'l',
+ 'o',
+ 'g',
+ 'd',
+ '.',
+ 'd',
+ 'a',
+ 'e',
+ 'm',
+ 'o',
+ 'n',
+ ':',
+ ' ',
+ 'r',
+ 'e',
+ 'i',
+ 'n',
+ 'i',
+ 't',
+ '\n' };
+ write(fdDmesg, reinit_message, sizeof(reinit_message));
+ }
+
+ // Anything that reads persist.<property>
+ if (logBuf) {
+ logBuf->init();
+ logBuf->initPrune(nullptr);
+ }
+ android::ReReadEventLogTags();
+ }
+
+ return nullptr;
+}
+
+char* android::uidToName(uid_t u) {
+ struct Userdata {
+ uid_t uid;
+ char* name;
+ } userdata = {
+ .uid = u,
+ .name = nullptr,
+ };
+
+ packagelist_parse(
+ [](pkg_info* info, void* callback_parameter) {
+ auto userdata = reinterpret_cast<Userdata*>(callback_parameter);
+ bool result = true;
+ if (info->uid == userdata->uid) {
+ userdata->name = strdup(info->name);
+ // false to stop processing
+ result = false;
+ }
+ packagelist_free(info);
+ return result;
+ },
+ &userdata);
+
+ return userdata.name;
+}
+
+// Serves as a global method to trigger reinitialization
+// and as a function that can be provided to signal().
+void reinit_signal_handler(int /*signal*/) {
+ sem_post(&reinit);
+}
+
+static void readDmesg(LogAudit* al, LogKlog* kl) {
+ if (!al && !kl) {
+ return;
+ }
+
+ int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
+ if (rc <= 0) {
+ return;
+ }
+
+ // Margin for additional input race or trailing nul
+ ssize_t len = rc + 1024;
+ std::unique_ptr<char[]> buf(new char[len]);
+
+ rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ if (rc <= 0) {
+ return;
+ }
+
+ if (rc < len) {
+ len = rc + 1;
+ }
+ buf[--len] = '\0';
+
+ if (kl && kl->isMonotonic()) {
+ kl->synchronize(buf.get(), len);
+ }
+
+ ssize_t sublen;
+ for (char *ptr = nullptr, *tok = buf.get();
+ (rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
+ tok = nullptr) {
+ if ((sublen <= 0) || !*tok) continue;
+ if (al) {
+ rc = al->log(tok, sublen);
+ }
+ if (kl) {
+ rc = kl->log(tok, sublen);
+ }
+ }
+}
+
+static int issueReinit() {
+ int sock = TEMP_FAILURE_RETRY(socket_local_client(
+ "logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
+ if (sock < 0) return -errno;
+
+ static const char reinitStr[] = "reinit";
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinitStr, sizeof(reinitStr)));
+ if (ret < 0) return -errno;
+
+ struct pollfd p;
+ memset(&p, 0, sizeof(p));
+ p.fd = sock;
+ p.events = POLLIN;
+ ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000));
+ if (ret < 0) return -errno;
+ if ((ret == 0) || !(p.revents & POLLIN)) return -ETIME;
+
+ static const char success[] = "success";
+ char buffer[sizeof(success) - 1];
+ memset(buffer, 0, sizeof(buffer));
+ ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
+ if (ret < 0) return -errno;
+
+ return strncmp(buffer, success, sizeof(success) - 1) != 0;
+}
+
+// Foreground waits for exit of the main persistent threads
+// that are started here. The threads are created to manage
+// UNIX domain client sockets for writing, reading and
+// controlling the user space logger, and for any additional
+// logging plugins like auditd and restart control. Additional
+// transitory per-client threads are created for each reader.
+int main(int argc, char* argv[]) {
+ // logd is written under the assumption that the timezone is UTC.
+ // If TZ is not set, persist.sys.timezone is looked up in some time utility
+ // libc functions, including mktime. It confuses the logd time handling,
+ // so here explicitly set TZ to UTC, which overrides the property.
+ setenv("TZ", "UTC", 1);
+ // issue reinit command. KISS argument parsing.
+ if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
+ return issueReinit();
+ }
+
+ static const char dev_kmsg[] = "/dev/kmsg";
+ fdDmesg = android_get_control_file(dev_kmsg);
+ if (fdDmesg < 0) {
+ fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
+ }
+
+ int fdPmesg = -1;
+ bool klogd = __android_logger_property_get_bool(
+ "ro.logd.kernel",
+ BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ if (klogd) {
+ static const char proc_kmsg[] = "/proc/kmsg";
+ fdPmesg = android_get_control_file(proc_kmsg);
+ if (fdPmesg < 0) {
+ fdPmesg = TEMP_FAILURE_RETRY(
+ open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));
+ }
+ if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
+ }
+
+ bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+ if (DropPrivs(klogd, auditd) != 0) {
+ return EXIT_FAILURE;
+ }
+
+ // Reinit Thread
+ sem_init(&reinit, 0, 0);
+ pthread_attr_t attr;
+ if (!pthread_attr_init(&attr)) {
+ struct sched_param param;
+
+ memset(&param, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, &param);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+ if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ pthread_t thread;
+ reinit_running = true;
+ if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
+ reinit_running = false;
+ }
+ }
+ pthread_attr_destroy(&attr);
+ }
+
+ // Serves the purpose of managing the last logs times read on a
+ // socket connection, and as a reader lock on a range of log
+ // entries.
+
+ LastLogTimes* times = new LastLogTimes();
+
+ // LogBuffer is the object which is responsible for holding all
+ // log entries.
+
+ logBuf = new LogBuffer(times);
+
+ signal(SIGHUP, reinit_signal_handler);
+
+ if (__android_logger_property_get_bool(
+ "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
+ BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE)) {
+ logBuf->enableStatistics();
+ }
+
+ // LogReader listens on /dev/socket/logdr. When a client
+ // connects, log entries in the LogBuffer are written to the client.
+
+ LogReader* reader = new LogReader(logBuf);
+ if (reader->startListener()) {
+ return EXIT_FAILURE;
+ }
+
+ // LogListener listens on /dev/socket/logdw for client
+ // initiated log messages. New log entries are added to LogBuffer
+ // and LogReader is notified to send updates to connected clients.
+
+ LogListener* swl = new LogListener(logBuf, reader);
+ // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
+ if (swl->startListener(600)) {
+ return EXIT_FAILURE;
+ }
+
+ // Command listener listens on /dev/socket/logd for incoming logd
+ // administrative commands.
+
+ CommandListener* cl = new CommandListener(logBuf, reader, swl);
+ if (cl->startListener()) {
+ return EXIT_FAILURE;
+ }
+
+ // LogAudit listens on NETLINK_AUDIT socket for selinux
+ // initiated log messages. New log entries are added to LogBuffer
+ // and LogReader is notified to send updates to connected clients.
+
+ LogAudit* al = nullptr;
+ if (auditd) {
+ al = new LogAudit(logBuf, reader,
+ __android_logger_property_get_bool(
+ "ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
+ ? fdDmesg
+ : -1);
+ }
+
+ LogKlog* kl = nullptr;
+ if (klogd) {
+ kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
+ }
+
+ readDmesg(al, kl);
+
+ // failure is an option ... messages are in dmesg (required by standard)
+
+ if (kl && kl->startListener()) {
+ delete kl;
+ }
+
+ if (al && al->startListener()) {
+ delete al;
+ }
+
+ TEMP_FAILURE_RETRY(pause());
+
+ return EXIT_SUCCESS;
+}
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
new file mode 100644
index 000000000..9a5defa17
--- /dev/null
+++ b/logd/tests/Android.bp
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "logd-unit-test-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+
+ "-DAUDITD_LOG_TAG=1003",
+ "-DCHATTY_LOG_TAG=1004",
+ ],
+
+ srcs: ["logd_test.cpp"],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "libselinux",
+ "liblog",
+ ],
+}
+
+// Build tests for the logger. Run with:
+// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+ name: "logd-unit-tests",
+ defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+ name: "CtsLogdTestCases",
+ defaults: ["logd-unit-test-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: [
+ "cts",
+ "vts10",
+ ],
+}
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
new file mode 100644
index 000000000..a25dc440f
--- /dev/null
+++ b/logd/tests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Logging Daemon test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsLogdTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
new file mode 100644
index 000000000..10bac62f3
--- /dev/null
+++ b/logd/tests/logd_test.cpp
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#ifdef __ANDROID__
+#include <selinux/selinux.h>
+#endif
+
+#include "../LogReader.h" // pickup LOGD_SNDTIMEO
+
+#ifdef __ANDROID__
+static void send_to_control(char* buf, size_t len) {
+ int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock >= 0) {
+ if (write(sock, buf, strlen(buf) + 1) > 0) {
+ ssize_t ret;
+ while ((ret = read(sock, buf, len)) > 0) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+ break;
+ }
+ len -= ret;
+ buf += ret;
+
+ struct pollfd p = {.fd = sock, .events = POLLIN, .revents = 0 };
+
+ ret = poll(&p, 1, 20);
+ if ((ret <= 0) || !(p.revents & POLLIN)) {
+ break;
+ }
+ }
+ }
+ close(sock);
+ }
+}
+
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char* buf, size_t len) {
+ snprintf(buf, len, "getStatistics 0 1 2 3 4");
+ send_to_control(buf, len);
+}
+
+static void alloc_statistics(char** buffer, size_t* length) {
+ size_t len = 8192;
+ char* buf;
+
+ for (int retry = 32; (retry >= 0); delete[] buf, --retry) {
+ buf = new char[len];
+ my_android_logger_get_statistics(buf, len);
+
+ buf[len - 1] = '\0';
+ size_t ret = atol(buf) + 1;
+ if (ret < 4) {
+ delete[] buf;
+ buf = nullptr;
+ break;
+ }
+ bool check = ret <= len;
+ len = ret;
+ if (check) {
+ break;
+ }
+ len += len / 8; // allow for some slop
+ }
+ *buffer = buf;
+ *length = len;
+}
+
+static char* find_benchmark_spam(char* cp) {
+ // liblog_benchmarks has been run designed to SPAM. The signature of
+ // a noisiest UID statistics is:
+ //
+ // Chattiest UIDs in main log buffer: Size Pruned
+ // UID PACKAGE BYTES LINES
+ // 0 root 54164 147569
+ //
+ char* benchmark = nullptr;
+ do {
+ static const char signature[] = "\n0 root ";
+
+ benchmark = strstr(cp, signature);
+ if (!benchmark) {
+ break;
+ }
+ cp = benchmark + sizeof(signature);
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ benchmark = cp;
+#ifdef DEBUG
+ char* end = strstr(benchmark, "\n");
+ if (end == nullptr) {
+ end = benchmark + strlen(benchmark);
+ }
+ fprintf(stderr, "parse for spam counter in \"%.*s\"\n",
+ (int)(end - benchmark), benchmark);
+#endif
+ // content
+ while (isdigit(*cp)) {
+ ++cp;
+ }
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ // optional +/- field?
+ if ((*cp == '-') || (*cp == '+')) {
+ while (isdigit(*++cp) || (*cp == '.') || (*cp == '%') ||
+ (*cp == 'X')) {
+ ;
+ }
+ while (isspace(*cp)) {
+ ++cp;
+ }
+ }
+ // number of entries pruned
+ unsigned long value = 0;
+ while (isdigit(*cp)) {
+ value = value * 10ULL + *cp - '0';
+ ++cp;
+ }
+ if (value > 10UL) {
+ break;
+ }
+ benchmark = nullptr;
+ } while (*cp);
+ return benchmark;
+}
+#endif
+
+TEST(logd, statistics) {
+#ifdef __ANDROID__
+ size_t len;
+ char* buf;
+
+ // Drop cache so that any access problems can be discovered.
+ if (!android::base::WriteStringToFile("3\n", "/proc/sys/vm/drop_caches")) {
+ GTEST_LOG_(INFO) << "Could not open trigger dropping inode cache";
+ }
+
+ alloc_statistics(&buf, &len);
+
+ ASSERT_TRUE(nullptr != buf);
+
+ // remove trailing FF
+ char* cp = buf + len - 1;
+ *cp = '\0';
+ bool truncated = *--cp != '\f';
+ if (!truncated) {
+ *cp = '\0';
+ }
+
+ // squash out the byte count
+ cp = buf;
+ if (!truncated) {
+ while (isdigit(*cp) || (*cp == '\n')) {
+ ++cp;
+ }
+ }
+
+ fprintf(stderr, "%s", cp);
+
+ EXPECT_LT((size_t)64, strlen(cp));
+
+ EXPECT_EQ(0, truncated);
+
+ char* main_logs = strstr(cp, "\nChattiest UIDs in main ");
+ EXPECT_TRUE(nullptr != main_logs);
+
+ char* radio_logs = strstr(cp, "\nChattiest UIDs in radio ");
+ if (!radio_logs)
+ GTEST_LOG_(INFO) << "Value of: nullptr != radio_logs\n"
+ "Actual: false\n"
+ "Expected: false\n";
+
+ char* system_logs = strstr(cp, "\nChattiest UIDs in system ");
+ EXPECT_TRUE(nullptr != system_logs);
+
+ char* events_logs = strstr(cp, "\nChattiest UIDs in events ");
+ EXPECT_TRUE(nullptr != events_logs);
+
+ // Check if there is any " u0_a#### " as this means packagelistparser broken
+ char* used_getpwuid = nullptr;
+ int used_getpwuid_len;
+ char* uid_name = cp;
+ static const char getpwuid_prefix[] = " u0_a";
+ while ((uid_name = strstr(uid_name, getpwuid_prefix)) != nullptr) {
+ used_getpwuid = uid_name + 1;
+ uid_name += strlen(getpwuid_prefix);
+ while (isdigit(*uid_name)) ++uid_name;
+ used_getpwuid_len = uid_name - used_getpwuid;
+ if (isspace(*uid_name)) break;
+ used_getpwuid = nullptr;
+ }
+ EXPECT_TRUE(nullptr == used_getpwuid);
+ if (used_getpwuid) {
+ fprintf(stderr, "libpackagelistparser failed to pick up %.*s\n",
+ used_getpwuid_len, used_getpwuid);
+ }
+
+ delete[] buf;
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static void caught_signal(int /* signum */) {
+}
+
+static void dump_log_msg(const char* prefix, log_msg* msg, int lid) {
+ std::cout << std::flush;
+ std::cerr << std::flush;
+ fflush(stdout);
+ fflush(stderr);
+ EXPECT_GE(msg->entry.hdr_size, sizeof(logger_entry));
+
+ fprintf(stderr, "%s: [%u] ", prefix, msg->len());
+ fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
+ fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid, msg->entry.sec,
+ msg->entry.nsec);
+ lid = msg->entry.lid;
+
+ switch (lid) {
+ case 0:
+ fprintf(stderr, "lid=main ");
+ break;
+ case 1:
+ fprintf(stderr, "lid=radio ");
+ break;
+ case 2:
+ fprintf(stderr, "lid=events ");
+ break;
+ case 3:
+ fprintf(stderr, "lid=system ");
+ break;
+ case 4:
+ fprintf(stderr, "lid=crash ");
+ break;
+ case 5:
+ fprintf(stderr, "lid=security ");
+ break;
+ case 6:
+ fprintf(stderr, "lid=kernel ");
+ break;
+ default:
+ if (lid >= 0) {
+ fprintf(stderr, "lid=%d ", lid);
+ }
+ }
+
+ unsigned int len = msg->entry.len;
+ fprintf(stderr, "msg[%u]={", len);
+ unsigned char* cp = reinterpret_cast<unsigned char*>(msg->msg());
+ if (!cp) {
+ static const unsigned char garbage[] = "<INVALID>";
+ cp = const_cast<unsigned char*>(garbage);
+ len = strlen(reinterpret_cast<const char*>(garbage));
+ }
+ while (len) {
+ unsigned char* p = cp;
+ while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) {
+ ++p;
+ }
+ if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) {
+ fprintf(stderr, "\"");
+ while (*cp) {
+ if (*cp != '\n') {
+ fprintf(stderr, "%c", *cp);
+ } else {
+ fprintf(stderr, "\\n");
+ }
+ ++cp;
+ --len;
+ }
+ fprintf(stderr, "\"");
+ } else {
+ fprintf(stderr, "%02x", *cp);
+ }
+ ++cp;
+ if (--len) {
+ fprintf(stderr, ", ");
+ }
+ }
+ fprintf(stderr, "}\n");
+ fflush(stderr);
+}
+#endif
+
+#ifdef __ANDROID__
+// BAD ROBOT
+// Benchmark threshold are generally considered bad form unless there is
+// is some human love applied to the continued maintenance and whether the
+// thresholds are tuned on a per-target basis. Here we check if the values
+// are more than double what is expected. Doubling will not prevent failure
+// on busy or low-end systems that could have a tendency to stretch values.
+//
+// The primary goal of this test is to simulate a spammy app (benchmark
+// being the worst) and check to make sure the logger can deal with it
+// appropriately by checking all the statistics are in an expected range.
+//
+TEST(logd, benchmark) {
+ size_t len;
+ char* buf;
+
+ alloc_statistics(&buf, &len);
+ bool benchmark_already_run = buf && find_benchmark_spam(buf);
+ delete[] buf;
+
+ if (benchmark_already_run) {
+ fprintf(stderr,
+ "WARNING: spam already present and too much history\n"
+ " false OK for prune by worst UID check\n");
+ }
+
+ FILE* fp;
+
+ // Introduce some extreme spam for the worst UID filter
+ ASSERT_TRUE(
+ nullptr !=
+ (fp = popen("/data/nativetest/liblog-benchmarks/liblog-benchmarks"
+ " BM_log_maximum_retry"
+ " BM_log_maximum"
+ " BM_clock_overhead"
+ " BM_log_print_overhead"
+ " BM_log_latency"
+ " BM_log_delay",
+ "r")));
+
+ char buffer[5120];
+
+ static const char* benchmarks[] = {
+ "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
+ "BM_log_print_overhead ", "BM_log_latency ", "BM_log_delay "
+ };
+ static const unsigned int log_maximum_retry = 0;
+ static const unsigned int log_maximum = 1;
+ static const unsigned int clock_overhead = 2;
+ static const unsigned int log_print_overhead = 3;
+ static const unsigned int log_latency = 4;
+ static const unsigned int log_delay = 5;
+
+ unsigned long ns[arraysize(benchmarks)];
+
+ memset(ns, 0, sizeof(ns));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ for (unsigned i = 0; i < arraysize(ns); ++i) {
+ char* cp = strstr(buffer, benchmarks[i]);
+ if (!cp) {
+ continue;
+ }
+ sscanf(cp, "%*s %lu %lu", &ns[i], &ns[i]);
+ fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]);
+ }
+ }
+ int ret = pclose(fp);
+
+ if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) {
+ fprintf(stderr,
+ "WARNING: "
+ "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n"
+ " can not perform test\n");
+ return;
+ }
+
+ EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
+ EXPECT_NE(0UL, ns[log_maximum_retry]); // failure to parse
+
+ EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
+ EXPECT_NE(0UL, ns[log_maximum]); // failure to parse
+
+ EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
+ EXPECT_NE(0UL, ns[clock_overhead]); // failure to parse
+
+ EXPECT_GE(250000UL, ns[log_print_overhead]); // 126886 user
+ EXPECT_NE(0UL, ns[log_print_overhead]); // failure to parse
+
+ EXPECT_GE(10000000UL,
+ ns[log_latency]); // 1453559 user space (background cgroup)
+ EXPECT_NE(0UL, ns[log_latency]); // failure to parse
+
+ EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
+ EXPECT_NE(0UL, ns[log_delay]); // failure to parse
+
+ alloc_statistics(&buf, &len);
+
+ bool collected_statistics = !!buf;
+ EXPECT_EQ(true, collected_statistics);
+
+ ASSERT_TRUE(nullptr != buf);
+
+ char* benchmark_statistics_found = find_benchmark_spam(buf);
+ ASSERT_TRUE(benchmark_statistics_found != nullptr);
+
+ // Check how effective the SPAM filter is, parse out Now size.
+ // 0 root 54164 147569
+ // ^-- benchmark_statistics_found
+
+ unsigned long nowSpamSize = atol(benchmark_statistics_found);
+
+ delete[] buf;
+
+ ASSERT_NE(0UL, nowSpamSize);
+
+ // Determine if we have the spam filter enabled
+ int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+
+ ASSERT_TRUE(sock >= 0);
+
+ static const char getPruneList[] = "getPruneList";
+ if (write(sock, getPruneList, sizeof(getPruneList)) > 0) {
+ char buffer[80];
+ memset(buffer, 0, sizeof(buffer));
+ read(sock, buffer, sizeof(buffer));
+ char* cp = strchr(buffer, '\n');
+ if (!cp || (cp[1] != '~') || (cp[2] != '!')) {
+ close(sock);
+ fprintf(stderr,
+ "WARNING: "
+ "Logger has SPAM filtration turned off \"%s\"\n",
+ buffer);
+ return;
+ }
+ } else {
+ int save_errno = errno;
+ close(sock);
+ FAIL() << "Can not send " << getPruneList << " to logger -- "
+ << strerror(save_errno);
+ }
+
+ static const unsigned long expected_absolute_minimum_log_size = 65536UL;
+ unsigned long totalSize = expected_absolute_minimum_log_size;
+ static const char getSize[] = { 'g', 'e', 't', 'L', 'o', 'g',
+ 'S', 'i', 'z', 'e', ' ', LOG_ID_MAIN + '0',
+ '\0' };
+ if (write(sock, getSize, sizeof(getSize)) > 0) {
+ char buffer[80];
+ memset(buffer, 0, sizeof(buffer));
+ read(sock, buffer, sizeof(buffer));
+ totalSize = atol(buffer);
+ if (totalSize < expected_absolute_minimum_log_size) {
+ fprintf(stderr,
+ "WARNING: "
+ "Logger had unexpected referenced size \"%s\"\n",
+ buffer);
+ totalSize = expected_absolute_minimum_log_size;
+ }
+ }
+ close(sock);
+
+ // logd allows excursions to 110% of total size
+ totalSize = (totalSize * 11) / 10;
+
+ // 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
+ ASSERT_GT(totalSize, nowSpamSize * 2);
+}
+#endif
+
+// b/26447386 confirm fixed
+void timeout_negative(const char* command) {
+#ifdef __ANDROID__
+ log_msg msg_wrap, msg_timeout;
+ bool content_wrap = false, content_timeout = false, written = false;
+ unsigned int alarm_wrap = 0, alarm_timeout = 0;
+ // A few tries to get it right just in case wrap kicks in due to
+ // content providers being active during the test.
+ int i = 3;
+
+ while (--i) {
+ int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ ASSERT_LT(0, fd);
+
+ std::string ask(command);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(3);
+
+ size_t len = ask.length() + 1;
+ written = write(fd, ask.c_str(), len) == (ssize_t)len;
+ if (!written) {
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
+ close(fd);
+ continue;
+ }
+
+ // alarm triggers at 50% of the --wrap time out
+ content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
+
+ alarm_wrap = alarm(5);
+
+ // alarm triggers at 133% of the --wrap time out
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ if (!content_timeout) { // make sure we hit dumpAndClose
+ content_timeout =
+ recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ }
+
+ if (old_alarm > 0) {
+ unsigned int time_spent = 3 - alarm_wrap;
+ if (old_alarm > time_spent + 1) {
+ old_alarm -= time_spent;
+ } else {
+ old_alarm = 2;
+ }
+ }
+ alarm_timeout = alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
+
+ close(fd);
+
+ if (content_wrap && alarm_wrap && content_timeout && alarm_timeout) {
+ break;
+ }
+ }
+
+ if (content_wrap) {
+ dump_log_msg("wrap", &msg_wrap, -1);
+ }
+
+ if (content_timeout) {
+ dump_log_msg("timeout", &msg_timeout, -1);
+ }
+
+ EXPECT_TRUE(written);
+ EXPECT_TRUE(content_wrap);
+ EXPECT_NE(0U, alarm_wrap);
+ EXPECT_TRUE(content_timeout);
+ EXPECT_NE(0U, alarm_timeout);
+#else
+ command = nullptr;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, timeout_no_start) {
+ timeout_negative("dumpAndClose lids=0,1,2,3,4,5 timeout=6");
+}
+
+TEST(logd, timeout_start_epoch) {
+ timeout_negative(
+ "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
+}
+
+#ifdef ENABLE_FLAKY_TESTS
+// b/26447386 refined behavior
+TEST(logd, timeout) {
+#ifdef __ANDROID__
+ // b/33962045 This test interferes with other log reader tests that
+ // follow because of file descriptor socket persistence in the same
+ // process. So let's fork it to isolate it from giving us pain.
+
+ pid_t pid = fork();
+
+ if (pid) {
+ siginfo_t info = {};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+ ASSERT_EQ(0, info.si_status);
+ return;
+ }
+
+ log_msg msg_wrap, msg_timeout;
+ bool content_wrap = false, content_timeout = false, written = false;
+ unsigned int alarm_wrap = 0, alarm_timeout = 0;
+ // A few tries to get it right just in case wrap kicks in due to
+ // content providers being active during the test.
+ int i = 5;
+ log_time start(android_log_clockid());
+ start.tv_sec -= 30; // reach back a moderate period of time
+
+ while (--i) {
+ int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ int save_errno = errno;
+ if (fd < 0) {
+ fprintf(stderr, "failed to open /dev/socket/logdr %s\n",
+ strerror(save_errno));
+ _exit(fd);
+ }
+
+ std::string ask = android::base::StringPrintf(
+ "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
+ ".%09" PRIu32,
+ start.tv_sec, start.tv_nsec);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(3);
+
+ size_t len = ask.length() + 1;
+ written = write(fd, ask.c_str(), len) == (ssize_t)len;
+ if (!written) {
+ alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
+ close(fd);
+ continue;
+ }
+
+ // alarm triggers at 50% of the --wrap time out
+ content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
+
+ alarm_wrap = alarm(5);
+
+ // alarm triggers at 133% of the --wrap time out
+ content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ if (!content_timeout) { // make sure we hit dumpAndClose
+ content_timeout =
+ recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
+ }
+
+ if (old_alarm > 0) {
+ unsigned int time_spent = 3 - alarm_wrap;
+ if (old_alarm > time_spent + 1) {
+ old_alarm -= time_spent;
+ } else {
+ old_alarm = 2;
+ }
+ }
+ alarm_timeout = alarm(old_alarm);
+ sigaction(SIGALRM, &old_sigaction, nullptr);
+
+ close(fd);
+
+ if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+ break;
+ }
+
+ // modify start time in case content providers are relatively
+ // active _or_ inactive during the test.
+ if (content_timeout) {
+ log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
+ if (msg < start) {
+ fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
+ msg_timeout.entry.nsec, (unsigned)start.tv_sec,
+ (unsigned)start.tv_nsec);
+ _exit(-1);
+ }
+ if (msg > start) {
+ start = msg;
+ start.tv_sec += 30;
+ log_time now = log_time(android_log_clockid());
+ if (start > now) {
+ start = now;
+ --start.tv_sec;
+ }
+ }
+ } else {
+ start.tv_sec -= 120; // inactive, reach further back!
+ }
+ }
+
+ if (content_wrap) {
+ dump_log_msg("wrap", &msg_wrap, -1);
+ }
+
+ if (content_timeout) {
+ dump_log_msg("timeout", &msg_timeout, -1);
+ }
+
+ if (content_wrap || !content_timeout) {
+ fprintf(stderr, "start=%" PRIu32 ".%09" PRIu32 "\n", start.tv_sec,
+ start.tv_nsec);
+ }
+
+ EXPECT_TRUE(written);
+ EXPECT_FALSE(content_wrap);
+ EXPECT_EQ(0U, alarm_wrap);
+ EXPECT_TRUE(content_timeout);
+ EXPECT_NE(0U, alarm_timeout);
+
+ _exit(!written + content_wrap + alarm_wrap + !content_timeout +
+ !alarm_timeout);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif
+
+// b/27242723 confirmed fixed
+TEST(logd, SNDTIMEO) {
+#ifdef __ANDROID__
+ static const unsigned sndtimeo =
+ LOGD_SNDTIMEO; // <sigh> it has to be done!
+ static const unsigned sleep_time = sndtimeo + 3;
+ static const unsigned alarm_time = sleep_time + 5;
+
+ int fd;
+
+ ASSERT_TRUE(
+ (fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET)) > 0);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(alarm_time);
+
+ static const char ask[] = "stream lids=0,1,2,3,4,5,6"; // all sources
+ bool reader_requested = write(fd, ask, sizeof(ask)) == sizeof(ask);
+ EXPECT_TRUE(reader_requested);
+
+ log_msg msg;
+ bool read_one = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+ EXPECT_TRUE(read_one);
+ if (read_one) {
+ dump_log_msg("user", &msg, -1);
+ }
+
+ fprintf(stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
+ sleep(sleep_time);
+
+ // flush will block if we did not trigger. if it did, last entry returns 0
+ int recv_ret;
+ do {
+ recv_ret = recv(fd, msg.buf, sizeof(msg), 0);
+ } while (recv_ret > 0);
+ int save_errno = (recv_ret < 0) ? errno : 0;
+
+ EXPECT_NE(0U, alarm(old_alarm));
+ sigaction(SIGALRM, &old_sigaction, nullptr);
+
+ EXPECT_EQ(0, recv_ret);
+ if (recv_ret > 0) {
+ dump_log_msg("user", &msg, -1);
+ }
+ EXPECT_EQ(0, save_errno);
+
+ close(fd);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_list) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "getEventTag name=*");
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char* cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 4096);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_42) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "getEventTag id=42");
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char* cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 16);
+ EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != nullptr);
+ EXPECT_TRUE(strstr(buffer, "answer") != nullptr);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_newentry) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"(new|1)\"",
+ name);
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char* cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 16);
+ EXPECT_TRUE(strstr(buffer, "\t(new|1)") != nullptr);
+ EXPECT_TRUE(strstr(buffer, name) != nullptr);
+// ToDo: also look for this in /data/misc/logd/event-log-tags and
+// /dev/event-log-tags.
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef __ANDROID__
+static inline uint32_t get4LE(const uint8_t* src) {
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static inline uint32_t get4LE(const char* src) {
+ return get4LE(reinterpret_cast<const uint8_t*>(src));
+}
+#endif
+
+void __android_log_btwrite_multiple__helper(int count) {
+#ifdef __ANDROID__
+ log_time ts(CLOCK_MONOTONIC);
+ usleep(100);
+ log_time ts1(CLOCK_MONOTONIC);
+
+ // We fork to create a unique pid for the submitted log messages
+ // so that we do not collide with the other _multiple_ tests.
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ // child
+ for (int i = count; i; --i) {
+ ASSERT_LT(
+ 0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ usleep(100);
+ }
+ ASSERT_LT(0,
+ __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+ usleep(1000000);
+
+ _exit(0);
+ }
+
+ siginfo_t info = {};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+ ASSERT_EQ(0, info.si_status);
+
+ struct logger_list* logger_list;
+ ASSERT_TRUE(nullptr !=
+ (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+ 0, pid)));
+
+ int expected_count = (count < 2) ? count : 2;
+ int expected_chatty_count = (count <= 2) ? 0 : 1;
+ int expected_identical_count = (count < 2) ? 0 : (count - 2);
+ static const int expected_expire_count = 0;
+
+ count = 0;
+ int second_count = 0;
+ int chatty_count = 0;
+ int identical_count = 0;
+ int expire_count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+ if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
+ (log_msg.id() != LOG_ID_EVENTS))
+ continue;
+
+ char* eventData = log_msg.msg();
+ if (!eventData) continue;
+
+ uint32_t tag = get4LE(eventData);
+
+ if ((eventData[4] == EVENT_TYPE_LONG) &&
+ (log_msg.entry.len == (4 + 1 + 8))) {
+ if (tag != 0) continue;
+
+ log_time tx(eventData + 4 + 1);
+ if (ts == tx) {
+ ++count;
+ } else if (ts1 == tx) {
+ ++second_count;
+ }
+ } else if (eventData[4] == EVENT_TYPE_STRING) {
+ if (tag != CHATTY_LOG_TAG) continue;
+ ++chatty_count;
+ // int len = get4LE(eventData + 4 + 1);
+ log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+ const char* cp;
+ if ((cp = strstr(eventData + 4 + 1 + 4, " identical "))) {
+ unsigned val = 0;
+ sscanf(cp, " identical %u lines", &val);
+ identical_count += val;
+ } else if ((cp = strstr(eventData + 4 + 1 + 4, " expire "))) {
+ unsigned val = 0;
+ sscanf(cp, " expire %u lines", &val);
+ expire_count += val;
+ }
+ }
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(expected_count, count);
+ EXPECT_EQ(1, second_count);
+ EXPECT_EQ(expected_chatty_count, chatty_count);
+ EXPECT_EQ(expected_identical_count, identical_count);
+ EXPECT_EQ(expected_expire_count, expire_count);
+#else
+ count = 0;
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, multiple_test_1) {
+ __android_log_btwrite_multiple__helper(1);
+}
+
+TEST(logd, multiple_test_2) {
+ __android_log_btwrite_multiple__helper(2);
+}
+
+TEST(logd, multiple_test_3) {
+ __android_log_btwrite_multiple__helper(3);
+}
+
+TEST(logd, multiple_test_10) {
+ __android_log_btwrite_multiple__helper(10);
+}
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
new file mode 100644
index 000000000..8851a47a4
--- /dev/null
+++ b/logwrapper/Android.bp
@@ -0,0 +1,70 @@
+cc_defaults {
+ name: "logwrapper_defaults",
+ cflags: [
+ "-Werror",
+ ],
+}
+
+// ========================================================
+// Static and shared library
+// ========================================================
+
+cc_library {
+ name: "liblogwrap",
+ defaults: ["logwrapper_defaults"],
+ recovery_available: true,
+ srcs: ["logwrap.cpp"],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+ header_libs: ["libbase_headers"],
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+}
+
+// ========================================================
+// Executable
+// ========================================================
+
+cc_defaults {
+ name: "logwrapper_common",
+ defaults: ["logwrapper_defaults"],
+ local_include_dirs: ["include"],
+ srcs: [
+ "logwrap.cpp",
+ "logwrapper.cpp",
+ ],
+ header_libs: ["libbase_headers"],
+ shared_libs: ["libcutils", "liblog"],
+}
+
+cc_binary {
+ name: "logwrapper",
+ defaults: ["logwrapper_common"],
+}
+
+cc_binary {
+ name: "logwrapper_vendor",
+ defaults: ["logwrapper_common"],
+ stem: "logwrapper",
+ vendor: true,
+}
+
+// ========================================================
+// Benchmark
+// ========================================================
+
+cc_benchmark {
+ name: "logwrap_fork_execvp_benchmark",
+ defaults: ["logwrapper_defaults"],
+ srcs: [
+ "logwrap_fork_execvp_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "liblogwrap",
+ ],
+}
diff --git a/logwrapper/NOTICE b/logwrapper/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/logwrapper/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/logwrapper/OWNERS b/logwrapper/OWNERS
new file mode 100644
index 000000000..babbe4ded
--- /dev/null
+++ b/logwrapper/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
new file mode 100644
index 000000000..cb40ee214
--- /dev/null
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -0,0 +1,61 @@
+/* system/core/include/logwrap/logwrap.h
+ *
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/*
+ * Run a command while logging its stdout and stderr
+ *
+ * Arguments:
+ * argc: the number of elements in argv
+ * argv: an array of strings containing the command to be executed and its
+ * arguments as separate strings. argv does not need to be
+ * NULL-terminated
+ * status: the equivalent child status as populated by wait(status). This
+ * value is only valid when logwrap successfully completes. If NULL
+ * the return value of the child will be the function's return value.
+ * forward_signals: set to true if you want to forward SIGINT, SIGQUIT, and
+ * SIGHUP to the child process, while it is running. You likely do
+ * not need to use this; it is primarily for the logwrapper
+ * executable itself.
+ * log_target: Specify where to log the output of the child, either LOG_NONE,
+ * LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
+ * log), or LOG_FILE (and you need to specify a pathname in the
+ * file_path argument, otherwise pass NULL). These are bit fields,
+ * and can be OR'ed together to log to multiple places.
+ * abbreviated: If true, capture up to the first 100 lines and last 4K of
+ * output from the child. The abbreviated output is not dumped to
+ * the specified log until the child has exited.
+ * file_path: if log_target has the LOG_FILE bit set, then this parameter
+ * must be set to the pathname of the file to log to.
+ *
+ * Return value:
+ * 0 when logwrap successfully run the child process and captured its status
+ * -1 when an internal error occurred
+ * -ECHILD if status is NULL and the child didn't exit properly
+ * the return value of the child if it exited properly and status is NULL
+ *
+ */
+
+/* Values for the log_target parameter logwrap_fork_execvp() */
+#define LOG_NONE 0
+#define LOG_ALOG 1
+#define LOG_KLOG 2
+#define LOG_FILE 4
+
+int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
+ int log_target, bool abbreviated, const char* file_path);
diff --git a/logwrapper/logwrap.cpp b/logwrapper/logwrap.cpp
new file mode 100644
index 000000000..5a518bc56
--- /dev/null
+++ b/logwrapper/logwrap.cpp
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <android-base/macros.h>
+#include <cutils/klog.h>
+#include <log/log.h>
+#include <logwrap/logwrap.h>
+
+static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
+// Protected by fd_mutex. These signals must be blocked while modifying as well.
+static pid_t child_pid;
+static struct sigaction old_int;
+static struct sigaction old_quit;
+static struct sigaction old_hup;
+
+#define ERROR(fmt, args...) \
+ do { \
+ fprintf(stderr, fmt, ##args); \
+ ALOG(LOG_ERROR, "logwrapper", fmt, ##args); \
+ } while (0)
+
+#define FATAL_CHILD(fmt, args...) \
+ do { \
+ ERROR(fmt, ##args); \
+ _exit(-1); \
+ } while (0)
+
+#define MAX_KLOG_TAG 16
+
+/* This is a simple buffer that holds up to the first beginning_buf->buf_size
+ * bytes of output from a command.
+ */
+#define BEGINNING_BUF_SIZE 0x1000
+struct beginning_buf {
+ char* buf;
+ size_t alloc_len;
+ /* buf_size is the usable space, which is one less than the allocated size */
+ size_t buf_size;
+ size_t used_len;
+};
+
+/* This is a circular buf that holds up to the last ending_buf->buf_size bytes
+ * of output from a command after the first beginning_buf->buf_size bytes
+ * (which are held in beginning_buf above).
+ */
+#define ENDING_BUF_SIZE 0x1000
+struct ending_buf {
+ char* buf;
+ ssize_t alloc_len;
+ /* buf_size is the usable space, which is one less than the allocated size */
+ ssize_t buf_size;
+ ssize_t used_len;
+ /* read and write offsets into the circular buffer */
+ int read;
+ int write;
+};
+
+/* A structure to hold all the abbreviated buf data */
+struct abbr_buf {
+ struct beginning_buf b_buf;
+ struct ending_buf e_buf;
+ int beginning_buf_full;
+};
+
+/* Collect all the various bits of info needed for logging in one place. */
+struct log_info {
+ int log_target;
+ char klog_fmt[MAX_KLOG_TAG * 2];
+ const char* btag;
+ bool abbreviated;
+ FILE* fp;
+ struct abbr_buf a_buf;
+};
+
+/* Forware declaration */
+static void add_line_to_abbr_buf(struct abbr_buf* a_buf, char* linebuf, int linelen);
+
+/* Return 0 on success, and 1 when full */
+static int add_line_to_linear_buf(struct beginning_buf* b_buf, char* line, ssize_t line_len) {
+ int full = 0;
+
+ if ((line_len + b_buf->used_len) > b_buf->buf_size) {
+ full = 1;
+ } else {
+ /* Add to the end of the buf */
+ memcpy(b_buf->buf + b_buf->used_len, line, line_len);
+ b_buf->used_len += line_len;
+ }
+
+ return full;
+}
+
+static void add_line_to_circular_buf(struct ending_buf* e_buf, char* line, ssize_t line_len) {
+ ssize_t free_len;
+ ssize_t needed_space;
+ int cnt;
+
+ if (e_buf->buf == nullptr) {
+ return;
+ }
+
+ if (line_len > e_buf->buf_size) {
+ return;
+ }
+
+ free_len = e_buf->buf_size - e_buf->used_len;
+
+ if (line_len > free_len) {
+ /* remove oldest entries at read, and move read to make
+ * room for the new string */
+ needed_space = line_len - free_len;
+ e_buf->read = (e_buf->read + needed_space) % e_buf->buf_size;
+ e_buf->used_len -= needed_space;
+ }
+
+ /* Copy the line into the circular buffer, dealing with possible
+ * wraparound.
+ */
+ cnt = std::min(line_len, e_buf->buf_size - e_buf->write);
+ memcpy(e_buf->buf + e_buf->write, line, cnt);
+ if (cnt < line_len) {
+ memcpy(e_buf->buf, line + cnt, line_len - cnt);
+ }
+ e_buf->used_len += line_len;
+ e_buf->write = (e_buf->write + line_len) % e_buf->buf_size;
+}
+
+/* Log directly to the specified log */
+static void do_log_line(struct log_info* log_info, const char* line) {
+ if (log_info->log_target & LOG_KLOG) {
+ klog_write(6, log_info->klog_fmt, line);
+ }
+ if (log_info->log_target & LOG_ALOG) {
+ ALOG(LOG_INFO, log_info->btag, "%s", line);
+ }
+ if (log_info->log_target & LOG_FILE) {
+ fprintf(log_info->fp, "%s\n", line);
+ }
+}
+
+/* Log to either the abbreviated buf, or directly to the specified log
+ * via do_log_line() above.
+ */
+static void log_line(struct log_info* log_info, char* line, int len) {
+ if (log_info->abbreviated) {
+ add_line_to_abbr_buf(&log_info->a_buf, line, len);
+ } else {
+ do_log_line(log_info, line);
+ }
+}
+
+/*
+ * The kernel will take a maximum of 1024 bytes in any single write to
+ * the kernel logging device file, so find and print each line one at
+ * a time. The allocated size for buf should be at least 1 byte larger
+ * than buf_size (the usable size of the buffer) to make sure there is
+ * room to temporarily stuff a null byte to terminate a line for logging.
+ */
+static void print_buf_lines(struct log_info* log_info, char* buf, int buf_size) {
+ char* line_start;
+ char c;
+ int i;
+
+ line_start = buf;
+ for (i = 0; i < buf_size; i++) {
+ if (*(buf + i) == '\n') {
+ /* Found a line ending, print the line and compute new line_start */
+ /* Save the next char and replace with \0 */
+ c = *(buf + i + 1);
+ *(buf + i + 1) = '\0';
+ do_log_line(log_info, line_start);
+ /* Restore the saved char */
+ *(buf + i + 1) = c;
+ line_start = buf + i + 1;
+ } else if (*(buf + i) == '\0') {
+ /* The end of the buffer, print the last bit */
+ do_log_line(log_info, line_start);
+ break;
+ }
+ }
+ /* If the buffer was completely full, and didn't end with a newline, just
+ * ignore the partial last line.
+ */
+}
+
+static void init_abbr_buf(struct abbr_buf* a_buf) {
+ char* new_buf;
+
+ memset(a_buf, 0, sizeof(struct abbr_buf));
+ new_buf = static_cast<char*>(malloc(BEGINNING_BUF_SIZE));
+ if (new_buf) {
+ a_buf->b_buf.buf = new_buf;
+ a_buf->b_buf.alloc_len = BEGINNING_BUF_SIZE;
+ a_buf->b_buf.buf_size = BEGINNING_BUF_SIZE - 1;
+ }
+ new_buf = static_cast<char*>(malloc(ENDING_BUF_SIZE));
+ if (new_buf) {
+ a_buf->e_buf.buf = new_buf;
+ a_buf->e_buf.alloc_len = ENDING_BUF_SIZE;
+ a_buf->e_buf.buf_size = ENDING_BUF_SIZE - 1;
+ }
+}
+
+static void free_abbr_buf(struct abbr_buf* a_buf) {
+ free(a_buf->b_buf.buf);
+ free(a_buf->e_buf.buf);
+}
+
+static void add_line_to_abbr_buf(struct abbr_buf* a_buf, char* linebuf, int linelen) {
+ if (!a_buf->beginning_buf_full) {
+ a_buf->beginning_buf_full = add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
+ }
+ if (a_buf->beginning_buf_full) {
+ add_line_to_circular_buf(&a_buf->e_buf, linebuf, linelen);
+ }
+}
+
+static void print_abbr_buf(struct log_info* log_info) {
+ struct abbr_buf* a_buf = &log_info->a_buf;
+
+ /* Add the abbreviated output to the kernel log */
+ if (a_buf->b_buf.alloc_len) {
+ print_buf_lines(log_info, a_buf->b_buf.buf, a_buf->b_buf.used_len);
+ }
+
+ /* Print an ellipsis to indicate that the buffer has wrapped or
+ * is full, and some data was not logged.
+ */
+ if (a_buf->e_buf.used_len == a_buf->e_buf.buf_size) {
+ do_log_line(log_info, "...\n");
+ }
+
+ if (a_buf->e_buf.used_len == 0) {
+ return;
+ }
+
+ /* Simplest way to print the circular buffer is allocate a second buf
+ * of the same size, and memcpy it so it's a simple linear buffer,
+ * and then cal print_buf_lines on it */
+ if (a_buf->e_buf.read < a_buf->e_buf.write) {
+ /* no wrap around, just print it */
+ print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read, a_buf->e_buf.used_len);
+ } else {
+ /* The circular buffer will always have at least 1 byte unused,
+ * so by allocating alloc_len here we will have at least
+ * 1 byte of space available as required by print_buf_lines().
+ */
+ char* nbuf = static_cast<char*>(malloc(a_buf->e_buf.alloc_len));
+ if (!nbuf) {
+ return;
+ }
+ int first_chunk_len = a_buf->e_buf.buf_size - a_buf->e_buf.read;
+ memcpy(nbuf, a_buf->e_buf.buf + a_buf->e_buf.read, first_chunk_len);
+ /* copy second chunk */
+ memcpy(nbuf + first_chunk_len, a_buf->e_buf.buf, a_buf->e_buf.write);
+ print_buf_lines(log_info, nbuf, first_chunk_len + a_buf->e_buf.write);
+ free(nbuf);
+ }
+}
+
+static void signal_handler(int signal_num);
+
+static void block_signals(sigset_t* oldset) {
+ sigset_t blockset;
+
+ sigemptyset(&blockset);
+ sigaddset(&blockset, SIGINT);
+ sigaddset(&blockset, SIGQUIT);
+ sigaddset(&blockset, SIGHUP);
+ pthread_sigmask(SIG_BLOCK, &blockset, oldset);
+}
+
+static void unblock_signals(sigset_t* oldset) {
+ pthread_sigmask(SIG_SETMASK, oldset, nullptr);
+}
+
+static void setup_signal_handlers(pid_t pid) {
+ struct sigaction handler = {.sa_handler = signal_handler};
+
+ child_pid = pid;
+ sigaction(SIGINT, &handler, &old_int);
+ sigaction(SIGQUIT, &handler, &old_quit);
+ sigaction(SIGHUP, &handler, &old_hup);
+}
+
+static void restore_signal_handlers() {
+ sigaction(SIGINT, &old_int, nullptr);
+ sigaction(SIGQUIT, &old_quit, nullptr);
+ sigaction(SIGHUP, &old_hup, nullptr);
+ child_pid = 0;
+}
+
+static void signal_handler(int signal_num) {
+ if (child_pid == 0 || kill(child_pid, signal_num) != 0) {
+ restore_signal_handlers();
+ raise(signal_num);
+ }
+}
+
+static int parent(const char* tag, int parent_read, pid_t pid, int* chld_sts, int log_target,
+ bool abbreviated, const char* file_path, bool forward_signals) {
+ int status = 0;
+ char buffer[4096];
+ struct pollfd poll_fds[] = {
+ {
+ .fd = parent_read,
+ .events = POLLIN,
+ },
+ };
+ int rc = 0;
+ int fd;
+
+ struct log_info log_info;
+
+ int a = 0; // start index of unprocessed data
+ int b = 0; // end index of unprocessed data
+ int sz;
+ bool found_child = false;
+ // There is a very small chance that opening child_ptty in the child will fail, but in this case
+ // POLLHUP will not be generated below. Therefore, we use a 1 second timeout for poll() until
+ // we receive a message from child_ptty. If this times out, we call waitpid() with WNOHANG to
+ // check the status of the child process and exit appropriately if it has terminated.
+ bool received_messages = false;
+ char tmpbuf[256];
+
+ log_info.btag = basename(tag);
+ if (!log_info.btag) {
+ log_info.btag = tag;
+ }
+
+ if (abbreviated && (log_target == LOG_NONE)) {
+ abbreviated = 0;
+ }
+ if (abbreviated) {
+ init_abbr_buf(&log_info.a_buf);
+ }
+
+ if (log_target & LOG_KLOG) {
+ snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), "<6>%.*s: %%s\n", MAX_KLOG_TAG,
+ log_info.btag);
+ }
+
+ if ((log_target & LOG_FILE) && !file_path) {
+ /* No file_path specified, clear the LOG_FILE bit */
+ log_target &= ~LOG_FILE;
+ }
+
+ if (log_target & LOG_FILE) {
+ fd = open(file_path, O_WRONLY | O_CREAT | O_CLOEXEC, 0664);
+ if (fd < 0) {
+ ERROR("Cannot log to file %s\n", file_path);
+ log_target &= ~LOG_FILE;
+ } else {
+ lseek(fd, 0, SEEK_END);
+ log_info.fp = fdopen(fd, "a");
+ }
+ }
+
+ log_info.log_target = log_target;
+ log_info.abbreviated = abbreviated;
+
+ while (!found_child) {
+ int timeout = received_messages ? -1 : 1000;
+ if (TEMP_FAILURE_RETRY(poll(poll_fds, arraysize(poll_fds), timeout)) < 0) {
+ ERROR("poll failed\n");
+ rc = -1;
+ goto err_poll;
+ }
+
+ if (poll_fds[0].revents & POLLIN) {
+ received_messages = true;
+ sz = TEMP_FAILURE_RETRY(read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+
+ sz += b;
+ // Log one line at a time
+ for (b = 0; b < sz; b++) {
+ if (buffer[b] == '\r') {
+ if (abbreviated) {
+ /* The abbreviated logging code uses newline as
+ * the line separator. Lucikly, the pty layer
+ * helpfully cooks the output of the command
+ * being run and inserts a CR before NL. So
+ * I just change it to NL here when doing
+ * abbreviated logging.
+ */
+ buffer[b] = '\n';
+ } else {
+ buffer[b] = '\0';
+ }
+ } else if (buffer[b] == '\n') {
+ buffer[b] = '\0';
+ log_line(&log_info, &buffer[a], b - a);
+ a = b + 1;
+ }
+ }
+
+ if (a == 0 && b == sizeof(buffer) - 1) {
+ // buffer is full, flush
+ buffer[b] = '\0';
+ log_line(&log_info, &buffer[a], b - a);
+ b = 0;
+ } else if (a != b) {
+ // Keep left-overs
+ b -= a;
+ memmove(buffer, &buffer[a], b);
+ a = 0;
+ } else {
+ a = 0;
+ b = 0;
+ }
+ }
+
+ if (!received_messages || (poll_fds[0].revents & POLLHUP)) {
+ int ret;
+ sigset_t oldset;
+
+ if (forward_signals) {
+ // Our signal handlers forward these signals to 'child_pid', but waitpid() may reap
+ // the child, so we must block these signals until we either 1) conclude that the
+ // child is still running or 2) determine the child has been reaped and we have
+ // reset the signals to their original disposition.
+ block_signals(&oldset);
+ }
+
+ int flags = (poll_fds[0].revents & POLLHUP) ? 0 : WNOHANG;
+ ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, flags));
+ if (ret < 0) {
+ rc = errno;
+ ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
+ goto err_waitpid;
+ }
+ if (ret > 0) {
+ found_child = true;
+ }
+
+ if (forward_signals) {
+ if (found_child) {
+ restore_signal_handlers();
+ }
+ unblock_signals(&oldset);
+ }
+ }
+ }
+
+ if (chld_sts != nullptr) {
+ *chld_sts = status;
+ } else {
+ if (WIFEXITED(status))
+ rc = WEXITSTATUS(status);
+ else
+ rc = -ECHILD;
+ }
+
+ // Flush remaining data
+ if (a != b) {
+ buffer[b] = '\0';
+ log_line(&log_info, &buffer[a], b - a);
+ }
+
+ /* All the output has been processed, time to dump the abbreviated output */
+ if (abbreviated) {
+ print_abbr_buf(&log_info);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s terminated by exit(%d)\n", log_info.btag,
+ WEXITSTATUS(status));
+ do_log_line(&log_info, tmpbuf);
+ }
+ } else {
+ if (WIFSIGNALED(status)) {
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s terminated by signal %d\n", log_info.btag,
+ WTERMSIG(status));
+ do_log_line(&log_info, tmpbuf);
+ } else if (WIFSTOPPED(status)) {
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s stopped by signal %d\n", log_info.btag,
+ WSTOPSIG(status));
+ do_log_line(&log_info, tmpbuf);
+ }
+ }
+
+err_waitpid:
+err_poll:
+ if (log_target & LOG_FILE) {
+ fclose(log_info.fp); /* Also closes underlying fd */
+ }
+ if (abbreviated) {
+ free_abbr_buf(&log_info.a_buf);
+ }
+ return rc;
+}
+
+static void child(int argc, const char* const* argv) {
+ // create null terminated argv_child array
+ char* argv_child[argc + 1];
+ memcpy(argv_child, argv, argc * sizeof(char*));
+ argv_child[argc] = nullptr;
+
+ if (execvp(argv_child[0], argv_child)) {
+ FATAL_CHILD("executing %s failed: %s\n", argv_child[0], strerror(errno));
+ }
+}
+
+int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
+ int log_target, bool abbreviated, const char* file_path) {
+ pid_t pid;
+ int parent_ptty;
+ sigset_t oldset;
+ int rc = 0;
+
+ rc = pthread_mutex_lock(&fd_mutex);
+ if (rc) {
+ ERROR("failed to lock signal_fd mutex\n");
+ goto err_lock;
+ }
+
+ /* Use ptty instead of socketpair so that STDOUT is not buffered */
+ parent_ptty = TEMP_FAILURE_RETRY(posix_openpt(O_RDWR | O_CLOEXEC));
+ if (parent_ptty < 0) {
+ ERROR("Cannot create parent ptty\n");
+ rc = -1;
+ goto err_open;
+ }
+
+ char child_devname[64];
+ if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+ ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {
+ ERROR("Problem with /dev/ptmx\n");
+ rc = -1;
+ goto err_ptty;
+ }
+
+ if (forward_signals) {
+ // Block these signals until we have the child pid and our signal handlers set up.
+ block_signals(&oldset);
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ ERROR("Failed to fork\n");
+ rc = -1;
+ goto err_fork;
+ } else if (pid == 0) {
+ pthread_mutex_unlock(&fd_mutex);
+ if (forward_signals) {
+ unblock_signals(&oldset);
+ }
+
+ setsid();
+
+ int child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR | O_CLOEXEC));
+ if (child_ptty < 0) {
+ FATAL_CHILD("Cannot open child_ptty: %s\n", strerror(errno));
+ }
+ close(parent_ptty);
+
+ dup2(child_ptty, 1);
+ dup2(child_ptty, 2);
+ close(child_ptty);
+
+ child(argc, argv);
+ } else {
+ if (forward_signals) {
+ setup_signal_handlers(pid);
+ unblock_signals(&oldset);
+ }
+
+ rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated, file_path,
+ forward_signals);
+
+ if (forward_signals) {
+ restore_signal_handlers();
+ }
+ }
+
+err_fork:
+ if (forward_signals) {
+ unblock_signals(&oldset);
+ }
+err_ptty:
+ close(parent_ptty);
+err_open:
+ pthread_mutex_unlock(&fd_mutex);
+err_lock:
+ return rc;
+}
diff --git a/logwrapper/logwrap_fork_execvp_benchmark.cpp b/logwrapper/logwrap_fork_execvp_benchmark.cpp
new file mode 100644
index 000000000..b2d0c7133
--- /dev/null
+++ b/logwrapper/logwrap_fork_execvp_benchmark.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logwrap/logwrap.h"
+
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
+
+static void BM_android_fork_execvp_ext(benchmark::State& state) {
+ const char* argv[] = {"/system/bin/echo", "hello", "world"};
+ const int argc = 3;
+ while (state.KeepRunning()) {
+ int rc = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_NONE, false, nullptr);
+ CHECK_EQ(0, rc);
+ }
+}
+BENCHMARK(BM_android_fork_execvp_ext);
+
+BENCHMARK_MAIN();
diff --git a/logwrapper/logwrapper.cpp b/logwrapper/logwrapper.cpp
new file mode 100644
index 000000000..7118d12cc
--- /dev/null
+++ b/logwrapper/logwrapper.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+#include <log/log.h>
+#include <logwrap/logwrap.h>
+
+void fatal(const char* msg) {
+ fprintf(stderr, "%s", msg);
+ ALOG(LOG_ERROR, "logwrapper", "%s", msg);
+ exit(-1);
+}
+
+void usage() {
+ fatal("Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
+ "\n"
+ "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
+ "the Android logging system. Tag is set to BINARY, priority is\n"
+ "always LOG_INFO.\n"
+ "\n"
+ "-a: Causes logwrapper to do abbreviated logging.\n"
+ " This logs up to the first 4K and last 4K of the command\n"
+ " being run, and logs the output when the command exits\n"
+ "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
+ " fault address is set to the status of wait()\n"
+ "-k: Causes logwrapper to log to the kernel log instead of\n"
+ " the Android system log\n");
+}
+
+int main(int argc, char* argv[]) {
+ int seg_fault_on_exit = 0;
+ int log_target = LOG_ALOG;
+ bool abbreviated = false;
+ int ch;
+ int status = 0xAAAA;
+ int rc;
+
+ while ((ch = getopt(argc, argv, "adk")) != -1) {
+ switch (ch) {
+ case 'a':
+ abbreviated = true;
+ break;
+ case 'd':
+ seg_fault_on_exit = 1;
+ break;
+ case 'k':
+ log_target = LOG_KLOG;
+ klog_set_level(6);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ }
+
+ rc = logwrap_fork_execvp(argc, &argv[0], &status, true, log_target, abbreviated, nullptr);
+ if (!rc) {
+ if (WIFEXITED(status))
+ rc = WEXITSTATUS(status);
+ else
+ rc = -ECHILD;
+ }
+
+ if (seg_fault_on_exit) {
+ uintptr_t fault_address = (uintptr_t)status;
+ *(int*)fault_address = 0; // causes SIGSEGV with fault_address = status
+ }
+
+ return rc;
+}
diff --git a/mini_keyctl/Android.bp b/mini_keyctl/Android.bp
deleted file mode 100644
index 417ddac18..000000000
--- a/mini_keyctl/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
- name: "libmini_keyctl_static",
- srcs: [
- "mini_keyctl_utils.cpp"
- ],
- shared_libs: [
- "libbase",
- "libkeyutils",
- ],
- cflags: ["-Werror", "-Wall", "-Wextra"],
- export_include_dirs: ["."],
-}
-
-cc_binary {
- name: "mini-keyctl",
- srcs: [
- "mini_keyctl.cpp",
- ],
- static_libs: [
- "libmini_keyctl_static",
- ],
- shared_libs: [
- "libbase",
- "libkeyutils",
- ],
- cflags: ["-Werror", "-Wall", "-Wextra"],
-}
diff --git a/mkbootfs/Android.bp b/mkbootfs/Android.bp
deleted file mode 100644
index cd2a6245a..000000000
--- a/mkbootfs/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2005 The Android Open Source Project
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary_host {
- name: "mkbootfs",
- srcs: ["mkbootfs.c"],
- cflags: ["-Werror"],
- static_libs: [
- "libbase",
- "libcutils",
- "liblog",
- ],
- dist: {
- targets: ["dist_files"],
- },
-}
diff --git a/mkbootfs/mkbootfs.c b/mkbootfs/mkbootfs.c
deleted file mode 100644
index 58153f3f5..000000000
--- a/mkbootfs/mkbootfs.c
+++ /dev/null
@@ -1,368 +0,0 @@
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-
-#include <stdarg.h>
-#include <fcntl.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
-
-/* NOTES
-**
-** - see buffer-format.txt from the linux kernel docs for
-** an explanation of this file format
-** - dotfiles are ignored
-** - directories named 'root' are ignored
-** - device notes, pipes, etc are not supported (error)
-*/
-
-void die(const char *why, ...)
-{
- va_list ap;
-
- va_start(ap, why);
- fprintf(stderr,"error: ");
- vfprintf(stderr, why, ap);
- fprintf(stderr,"\n");
- va_end(ap);
- exit(1);
-}
-
-struct fs_config_entry {
- char* name;
- int uid, gid, mode;
-};
-
-static struct fs_config_entry* canned_config = NULL;
-static char *target_out_path = NULL;
-
-/* Each line in the canned file should be a path plus three ints (uid,
- * gid, mode). */
-#ifdef PATH_MAX
-#define CANNED_LINE_LENGTH (PATH_MAX+100)
-#else
-#define CANNED_LINE_LENGTH (1024)
-#endif
-
-#define TRAILER "TRAILER!!!"
-
-static int verbose = 0;
-static int total_size = 0;
-
-static void fix_stat(const char *path, struct stat *s)
-{
- uint64_t capabilities;
- if (canned_config) {
- // Use the list of file uid/gid/modes loaded from the file
- // given with -f.
-
- struct fs_config_entry* empty_path_config = NULL;
- struct fs_config_entry* p;
- for (p = canned_config; p->name; ++p) {
- if (!p->name[0]) {
- empty_path_config = p;
- }
- if (strcmp(p->name, path) == 0) {
- s->st_uid = p->uid;
- s->st_gid = p->gid;
- s->st_mode = p->mode | (s->st_mode & ~07777);
- return;
- }
- }
- s->st_uid = empty_path_config->uid;
- s->st_gid = empty_path_config->gid;
- s->st_mode = empty_path_config->mode | (s->st_mode & ~07777);
- } else {
- // Use the compiled-in fs_config() function.
- unsigned st_mode = s->st_mode;
- int is_dir = S_ISDIR(s->st_mode) || strcmp(path, TRAILER) == 0;
- fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities);
- s->st_mode = (typeof(s->st_mode)) st_mode;
- }
-}
-
-static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
-{
- // Nothing is special about this value, just picked something in the
- // approximate range that was being used already, and avoiding small
- // values which may be special.
- static unsigned next_inode = 300000;
-
- while(total_size & 3) {
- total_size++;
- putchar(0);
- }
-
- fix_stat(out, s);
-// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
-
- printf("%06x%08x%08x%08x%08x%08x%08x"
- "%08x%08x%08x%08x%08x%08x%08x%s%c",
- 0x070701,
- next_inode++, // s.st_ino,
- s->st_mode,
- 0, // s.st_uid,
- 0, // s.st_gid,
- 1, // s.st_nlink,
- 0, // s.st_mtime,
- datasize,
- 0, // volmajor
- 0, // volminor
- 0, // devmajor
- 0, // devminor,
- olen + 1,
- 0,
- out,
- 0
- );
-
- total_size += 6 + 8*13 + olen + 1;
-
- if(strlen(out) != (unsigned int)olen) die("ACK!");
-
- while(total_size & 3) {
- total_size++;
- putchar(0);
- }
-
- if(datasize) {
- fwrite(data, datasize, 1, stdout);
- total_size += datasize;
- }
-}
-
-static void _eject_trailer()
-{
- struct stat s;
- memset(&s, 0, sizeof(s));
- _eject(&s, TRAILER, 10, 0, 0);
-
- while(total_size & 0xff) {
- total_size++;
- putchar(0);
- }
-}
-
-static void _archive(char *in, char *out, int ilen, int olen);
-
-static int compare(const void* a, const void* b) {
- return strcmp(*(const char**)a, *(const char**)b);
-}
-
-static void _archive_dir(char *in, char *out, int ilen, int olen)
-{
- int i, t;
- DIR *d;
- struct dirent *de;
-
- if(verbose) {
- fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
- in, out, ilen, olen);
- }
-
- d = opendir(in);
- if(d == 0) die("cannot open directory '%s'", in);
-
- int size = 32;
- int entries = 0;
- char** names = malloc(size * sizeof(char*));
- if (names == NULL) {
- fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
- exit(1);
- }
-
- while((de = readdir(d)) != 0){
- /* xxx: feature? maybe some dotfiles are okay */
- if(de->d_name[0] == '.') continue;
-
- /* xxx: hack. use a real exclude list */
- if(!strcmp(de->d_name, "root")) continue;
-
- if (entries >= size) {
- size *= 2;
- names = realloc(names, size * sizeof(char*));
- if (names == NULL) {
- fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
- size);
- exit(1);
- }
- }
- names[entries] = strdup(de->d_name);
- if (names[entries] == NULL) {
- fprintf(stderr, "failed to strdup name \"%s\"\n",
- de->d_name);
- exit(1);
- }
- ++entries;
- }
-
- qsort(names, entries, sizeof(char*), compare);
-
- for (i = 0; i < entries; ++i) {
- t = strlen(names[i]);
- in[ilen] = '/';
- memcpy(in + ilen + 1, names[i], t + 1);
-
- if(olen > 0) {
- out[olen] = '/';
- memcpy(out + olen + 1, names[i], t + 1);
- _archive(in, out, ilen + t + 1, olen + t + 1);
- } else {
- memcpy(out, names[i], t + 1);
- _archive(in, out, ilen + t + 1, t);
- }
-
- in[ilen] = 0;
- out[olen] = 0;
-
- free(names[i]);
- }
- free(names);
-
- closedir(d);
-}
-
-static void _archive(char *in, char *out, int ilen, int olen)
-{
- struct stat s;
-
- if(verbose) {
- fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
- in, out, ilen, olen);
- }
-
- if(lstat(in, &s)) die("could not stat '%s'\n", in);
-
- if(S_ISREG(s.st_mode)){
- char *tmp;
- int fd;
-
- fd = open(in, O_RDONLY);
- if(fd < 0) die("cannot open '%s' for read", in);
-
- tmp = (char*) malloc(s.st_size);
- if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
-
- if(read(fd, tmp, s.st_size) != s.st_size) {
- die("cannot read %d bytes", s.st_size);
- }
-
- _eject(&s, out, olen, tmp, s.st_size);
-
- free(tmp);
- close(fd);
- } else if(S_ISDIR(s.st_mode)) {
- _eject(&s, out, olen, 0, 0);
- _archive_dir(in, out, ilen, olen);
- } else if(S_ISLNK(s.st_mode)) {
- char buf[1024];
- int size;
- size = readlink(in, buf, 1024);
- if(size < 0) die("cannot read symlink '%s'", in);
- _eject(&s, out, olen, buf, size);
- } else {
- die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
- }
-}
-
-void archive(const char *start, const char *prefix)
-{
- char in[8192];
- char out[8192];
-
- strcpy(in, start);
- strcpy(out, prefix);
-
- _archive_dir(in, out, strlen(in), strlen(out));
-}
-
-static void read_canned_config(char* filename)
-{
- int allocated = 8;
- int used = 0;
-
- canned_config =
- (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
-
- char line[CANNED_LINE_LENGTH];
- FILE* f = fopen(filename, "r");
- if (f == NULL) die("failed to open canned file");
-
- while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
- if (!line[0]) break;
- if (used >= allocated) {
- allocated *= 2;
- canned_config = (struct fs_config_entry*)realloc(
- canned_config, allocated * sizeof(struct fs_config_entry));
- if (canned_config == NULL) die("failed to reallocate memory");
- }
-
- struct fs_config_entry* cc = canned_config + used;
-
- if (isspace(line[0])) {
- cc->name = strdup("");
- cc->uid = atoi(strtok(line, " \n"));
- } else {
- cc->name = strdup(strtok(line, " \n"));
- cc->uid = atoi(strtok(NULL, " \n"));
- }
- cc->gid = atoi(strtok(NULL, " \n"));
- cc->mode = strtol(strtok(NULL, " \n"), NULL, 8);
- ++used;
- }
- if (used >= allocated) {
- ++allocated;
- canned_config = (struct fs_config_entry*)realloc(
- canned_config, allocated * sizeof(struct fs_config_entry));
- if (canned_config == NULL) die("failed to reallocate memory");
- }
- canned_config[used].name = NULL;
-
- fclose(f);
-}
-
-
-int main(int argc, char *argv[])
-{
- argc--;
- argv++;
-
- if (argc > 1 && strcmp(argv[0], "-d") == 0) {
- target_out_path = argv[1];
- argc -= 2;
- argv += 2;
- }
-
- if (argc > 1 && strcmp(argv[0], "-f") == 0) {
- read_canned_config(argv[1]);
- argc -= 2;
- argv += 2;
- }
-
- if(argc == 0) die("no directories to process?!");
-
- while(argc-- > 0){
- char *x = strchr(*argv, '=');
- if(x != 0) {
- *x++ = 0;
- } else {
- x = "";
- }
-
- archive(*argv, x);
-
- argv++;
- }
-
- _eject_trailer();
-
- return 0;
-}
diff --git a/property_service/OWNERS b/property_service/OWNERS
index 7529cb920..babbe4ded 100644
--- a/property_service/OWNERS
+++ b/property_service/OWNERS
@@ -1 +1 @@
-include platform/system/core:/janitors/OWNERS
+tomcherry@google.com
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 68614566b..108d15aa4 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -1,17 +1,13 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libpropertyinfoparser",
host_supported: true,
vendor_available: true,
ramdisk_available: true,
- vendor_ramdisk_available: true,
recovery_available: true,
native_bridge_supported: true,
srcs: ["property_info_parser.cpp"],
+ cpp_std: "experimental",
cppflags: [
"-Wall",
"-Wextra",
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index f260450d9..aa02a3a29 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -1,10 +1,7 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "propertyinfoserializer_defaults",
host_supported: true,
+ cpp_std: "experimental",
cppflags: [
"-Wall",
"-Wextra",
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 3907413cc..a64306280 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -139,7 +139,7 @@ TEST(propertyinfoserializer, GetPropertyInfo) {
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
- // Smoke test
+ // Sanity check
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index d26e35909..65e660a21 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -1,11 +1,8 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "property_info_checker",
host_supported: true,
static_executable: true,
+ cpp_std: "experimental",
static_libs: [
"libpropertyinfoserializer",
"libpropertyinfoparser",
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
new file mode 100644
index 000000000..ad86a4e6d
--- /dev/null
+++ b/qemu_pipe/Android.bp
@@ -0,0 +1,20 @@
+// Copyright 2011 The Android Open Source Project
+
+cc_library_static {
+ name: "libqemu_pipe",
+ vendor_available: true,
+ recovery_available: true,
+ apex_available: [
+ "com.android.adbd",
+ // TODO(b/151398197) remove the below
+ "//apex_available:platform",
+ ],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ srcs: ["qemu_pipe.cpp"],
+ local_include_dirs: ["include"],
+ static_libs: ["libbase"],
+ export_include_dirs: ["include"],
+ cflags: ["-Werror"],
+}
diff --git a/qemu_pipe/OWNERS b/qemu_pipe/OWNERS
new file mode 100644
index 000000000..dbc1bf60e
--- /dev/null
+++ b/qemu_pipe/OWNERS
@@ -0,0 +1 @@
+bohu@google.com
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
new file mode 100644
index 000000000..098749899
--- /dev/null
+++ b/qemu_pipe/include/qemu_pipe.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
new file mode 100644
index 000000000..beeccb07f
--- /dev/null
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "qemu_pipe.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <android-base/file.h>
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+# define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+ // Sanity check.
+ if (!pipeName) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+ strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+
+ // now, add 'pipe:' prefix and try again
+ // Note: host side will wait for the trailing '\0' to start
+ // service lookup.
+ const char pipe_prefix[] = "pipe:";
+ if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+ WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+ QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
+ __FUNCTION__, pipeName, strerror(errno));
+ close(fd);
+ return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ if (!WriteFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ if (!WriteFully(fd, buff, len)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ if (!ReadFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+ len);
+ return -1;
+ }
+ if (!ReadFully(fd, buff, size)) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+ strerror(errno));
+ return -1;
+ }
+ return size;
+}
diff --git a/reboot/Android.bp b/reboot/Android.bp
index 7b243bd56..cc717234e 100644
--- a/reboot/Android.bp
+++ b/reboot/Android.bp
@@ -1,9 +1,5 @@
// Copyright 2013 The Android Open Source Project
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "reboot",
srcs: ["reboot.c"],
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index ae21633da..96b5e0db4 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -12,18 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
prebuilt_etc {
name: "init.rc",
src: "init.rc",
sub_dir: "init/hw",
- required: [
- "fsverity_init",
- "platform-bootclasspath",
- ],
+ required: ["fsverity_init"],
}
prebuilt_etc {
@@ -31,18 +24,3 @@ prebuilt_etc {
src: "ueventd.rc",
recovery_available: true,
}
-
-// TODO(b/147210213) Generate list of libraries during build and fill in at build time
-linker_config {
- name: "system_linker_config",
- src: "etc/linker.config.json",
- installable: false,
-}
-
-// TODO(b/185211376) Scope the native APIs that microdroid will provide to the app payload
-prebuilt_etc {
- name: "public.libraries.android.txt",
- src: "etc/public.libraries.android.txt",
- filename: "public.libraries.txt",
- installable: false,
-} \ No newline at end of file
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 99d8f9a83..a9d0ed08a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -5,8 +5,6 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := init-debug.rc
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
@@ -20,8 +18,6 @@ ifneq ($(filter address,$(SANITIZE_TARGET)),)
include $(CLEAR_VARS)
LOCAL_MODULE := asan.options
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_PATH := $(TARGET_OUT)
@@ -33,8 +29,6 @@ ASAN_EXTRACT_FILES :=
ifeq ($(SANITIZE_TARGET_SYSTEM),true)
include $(CLEAR_VARS)
LOCAL_MODULE:= asan_extract
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := asan_extract.sh
@@ -53,8 +47,6 @@ endif
include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE := init.environ.rc
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
EXPORT_GLOBAL_ASAN_OPTIONS :=
@@ -77,7 +69,7 @@ endif
EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
ifeq ($(CLANG_COVERAGE),true)
- EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
endif
# Put it here instead of in init.rc module definition,
@@ -86,7 +78,7 @@ endif
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
- linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ linkerconfig $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -128,27 +120,6 @@ LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/ov
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
-
-# For /vendor_dlkm partition.
-LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
-# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
-# both devices with and without /vendor_dlkm partition. Those symlinks are for
-# devices without /vendor_dlkm partition. For devices with /vendor_dlkm
-# partition, mount vendor_dlkm.img under /vendor_dlkm will hide those symlinks.
-# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
-# via /vendor/lib/modules directly.
-LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
-
-# For /odm_dlkm partition.
-LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm
-# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
-# both devices with and without /odm_dlkm partition. Those symlinks are for
-# devices without /odm_dlkm partition. For devices with /odm_dlkm
-# partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
-# Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
-# via /odm/lib/modules directly.
-LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
-
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
@@ -161,6 +132,11 @@ ifdef BOARD_ROOT_EXTRA_SYMLINKS
; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
endif
+# The A/B updater uses a top-level /postinstall directory to mount the new
+# system before reboot.
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
+endif
# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
# Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
@@ -171,7 +147,9 @@ include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
- $(hide) cp $< $@
+ $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+ $(hide) sed -i -e 's?%DEX2OATBOOTCLASSPATH%?$(PRODUCT_DEX2OAT_BOOTCLASSPATH)?g' $@
+ $(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
@@ -188,8 +166,6 @@ endef
# sanitizer.libraries.txt
include $(CLEAR_VARS)
LOCAL_MODULE := sanitizer.libraries.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
@@ -214,8 +190,6 @@ $(LOCAL_BUILT_MODULE):
# adb_debug.prop in debug ramdisk
include $(CLEAR_VARS)
LOCAL_MODULE := adb_debug.prop
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index 5d0d6735f..ca22eb8ea 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,7 +1,5 @@
-bowgotsai@google.com
ccross@google.com
-dvander@google.com
-elsk@google.com
jeffv@google.com
jiyong@google.com
smoreland@google.com
+tomcherry@google.com
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
index cfc59a71b..85d27861d 100644
--- a/rootdir/avb/Android.bp
+++ b/rootdir/avb/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
filegroup {
name: "q-gsi_avbpubkey",
srcs: [
@@ -22,10 +18,3 @@ filegroup {
"s-gsi.avbpubkey",
],
}
-
-filegroup {
- name: "qcar-gsi_avbpubkey",
- srcs: [
- "qcar-gsi.avbpubkey",
- ],
-}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 647cfa21b..f96ffddb3 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -1,29 +1,17 @@
LOCAL_PATH:= $(call my-dir)
-ifeq ($(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT),true) # AVB keys are installed to vendor ramdisk
- ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) # no dedicated recovery partition
- my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/first_stage_ramdisk/avb
- else # device has a dedicated recovery partition
- my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/avb
- endif
-else
- ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) # no dedicated recovery partition
- my_gsi_avb_keys_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
- else # device has a dedicated recovery partition
- my_gsi_avb_keys_path := $(TARGET_RAMDISK_OUT)/avb
- endif
-endif
-
#######################################
# q-gsi.avbpubkey
include $(CLEAR_VARS)
LOCAL_MODULE := q-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
include $(BUILD_PREBUILT)
@@ -32,11 +20,13 @@ include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := q-developer-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
include $(BUILD_PREBUILT)
@@ -45,11 +35,13 @@ include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := r-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
include $(BUILD_PREBUILT)
@@ -58,11 +50,13 @@ include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := r-developer-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
include $(BUILD_PREBUILT)
@@ -71,11 +65,13 @@ include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := s-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
include $(BUILD_PREBUILT)
@@ -84,25 +80,12 @@ include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := s-developer-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-#######################################
-# qcar-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := qcar-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
include $(BUILD_PREBUILT)
-
-my_gsi_avb_keys_path :=
diff --git a/rootdir/avb/qcar-gsi.avbpubkey b/rootdir/avb/qcar-gsi.avbpubkey
deleted file mode 100644
index ce56646d0..000000000
--- a/rootdir/avb/qcar-gsi.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
deleted file mode 100644
index c58f29828..000000000
--- a/rootdir/etc/linker.config.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "requireLibs": [
- "libandroidicu.so",
- "libdexfile.so",
- "libdexfiled.so",
- "libicu.so",
- "libjdwp.so",
- "libnativebridge.so",
- "libnativehelper.so",
- "libnativeloader.so",
- "libsigchain.so",
- // TODO(b/122876336): Remove libpac.so once it's migrated to Webview
- "libpac.so",
- // TODO(b/120786417 or b/134659294): libicuuc.so
- // and libicui18n.so are kept for app compat.
- "libicui18n.so",
- "libicuuc.so",
- // resolv
- "libnetd_resolv.so",
- // nn
- "libneuralnetworks.so",
- // statsd
- "libstatspull.so",
- "libstatssocket.so",
- // adbd
- "libadb_pairing_auth.so",
- "libadb_pairing_connection.so",
- "libadb_pairing_server.so"
- ]
-}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 967205f93..405f5a9fc 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -10,14 +10,12 @@ libEGL.so
libGLESv1_CM.so
libGLESv2.so
libGLESv3.so
-libicu.so
libicui18n.so
libicuuc.so
libjnigraphics.so
liblog.so
libmediandk.so
libm.so
-libnativehelper.so
libnativewindow.so
libneuralnetworks.so nopreload
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index 77f8bb802..b56534070 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -17,7 +17,6 @@ libjnigraphics.so
liblog.so
libmediandk.so
libm.so
-libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 82196e4e8..7cbda0812 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -16,7 +16,6 @@ libjnigraphics.so
liblog.so
libmediandk.so
libm.so
-libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/init-debug.rc b/rootdir/init-debug.rc
index 77a80cd26..435d4cb25 100644
--- a/rootdir/init-debug.rc
+++ b/rootdir/init-debug.rc
@@ -6,11 +6,3 @@ on property:persist.mmc.max_write_speed=*
on property:persist.mmc.cache_size=*
write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}
-
-on early-init && property:ro.product.debugfs_restrictions.enabled=true
- mount debugfs debugfs /sys/kernel/debug
- chmod 0755 /sys/kernel/debug
-
-on property:sys.boot_completed=1 && property:ro.product.debugfs_restrictions.enabled=true && \
- property:persist.dbg.keep_debugfs_mounted=""
- umount /sys/kernel/debug
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index bf6e986c4..fdaaf1abe 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -10,6 +10,9 @@ on early-init
export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
export EXTERNAL_STORAGE /sdcard
export ASEC_MOUNTPOINT /mnt/asec
+ export BOOTCLASSPATH %BOOTCLASSPATH%
+ export DEX2OATBOOTCLASSPATH %DEX2OATBOOTCLASSPATH%
+ export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 376a678a8..510d1e911 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -56,7 +56,7 @@ on early-init
write /sys/module/dm_verity/parameters/prefetch_cluster 0
# Generate ld.config.txt for early executed processes
- exec -- /system/bin/bootstrap/linkerconfig --target /linkerconfig/bootstrap
+ exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap
chmod 644 /linkerconfig/bootstrap/ld.config.txt
copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
chmod 644 /linkerconfig/default/ld.config.txt
@@ -81,11 +81,6 @@ on early-init
# Mount tracefs
mount tracefs tracefs /sys/kernel/tracing
- # create sys dirctory
- mkdir /dev/sys 0755 system system
- mkdir /dev/sys/fs 0755 system system
- mkdir /dev/sys/block 0755 system system
-
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
@@ -148,55 +143,6 @@ on init
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
- # cpuctl hierarchy for devices using utilclamp
- mkdir /dev/cpuctl/foreground
- mkdir /dev/cpuctl/background
- mkdir /dev/cpuctl/top-app
- mkdir /dev/cpuctl/rt
- mkdir /dev/cpuctl/system
- mkdir /dev/cpuctl/system-background
- chown system system /dev/cpuctl
- chown system system /dev/cpuctl/foreground
- chown system system /dev/cpuctl/background
- chown system system /dev/cpuctl/top-app
- chown system system /dev/cpuctl/rt
- chown system system /dev/cpuctl/system
- chown system system /dev/cpuctl/system-background
- chown system system /dev/cpuctl/tasks
- chown system system /dev/cpuctl/foreground/tasks
- chown system system /dev/cpuctl/background/tasks
- chown system system /dev/cpuctl/top-app/tasks
- chown system system /dev/cpuctl/rt/tasks
- chown system system /dev/cpuctl/system/tasks
- chown system system /dev/cpuctl/system-background/tasks
- chmod 0664 /dev/cpuctl/tasks
- chmod 0664 /dev/cpuctl/foreground/tasks
- chmod 0664 /dev/cpuctl/background/tasks
- chmod 0664 /dev/cpuctl/top-app/tasks
- chmod 0664 /dev/cpuctl/rt/tasks
- chmod 0664 /dev/cpuctl/system/tasks
- chmod 0664 /dev/cpuctl/system-background/tasks
-
- # Create a cpu group for NNAPI HAL processes
- mkdir /dev/cpuctl/nnapi-hal
- chown system system /dev/cpuctl/nnapi-hal
- chown system system /dev/cpuctl/nnapi-hal/tasks
- chmod 0664 /dev/cpuctl/nnapi-hal/tasks
- write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
- write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
-
- # Create a cpu group for camera daemon processes
- mkdir /dev/cpuctl/camera-daemon
- chown system system /dev/cpuctl/camera-daemon
- chown system system /dev/cpuctl/camera-daemon/tasks
- chmod 0664 /dev/cpuctl/camera-daemon/tasks
-
- # Create an stune group for camera-specific processes
- mkdir /dev/stune/camera-daemon
- chown system system /dev/stune/camera-daemon
- chown system system /dev/stune/camera-daemon/tasks
- chmod 0664 /dev/stune/camera-daemon/tasks
-
# Create an stune group for NNAPI HAL processes
mkdir /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal
@@ -217,7 +163,6 @@ on init
chmod 0664 /dev/blkio/background/tasks
write /dev/blkio/blkio.weight 1000
write /dev/blkio/background/blkio.weight 200
- write /dev/blkio/background/blkio.bfq.weight 10
write /dev/blkio/blkio.group_idle 0
write /dev/blkio/background/blkio.group_idle 0
@@ -296,6 +241,8 @@ on init
write /proc/sys/vm/mmap_min_addr 32768
write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
write /proc/sys/net/unix/max_dgram_qlen 600
+ write /proc/sys/kernel/sched_rt_runtime_us 950000
+ write /proc/sys/kernel/sched_rt_period_us 1000000
# Assign reasonable ceiling values for socket rcv/snd buffers.
# These should almost always be overridden by the target per the
@@ -317,6 +264,13 @@ on init
# /proc/net/fib_trie leaks interface IP addresses
chmod 0400 /proc/net/fib_trie
+ # Create cgroup mount points for process groups
+ chown system system /dev/cpuctl
+ chown system system /dev/cpuctl/tasks
+ chmod 0666 /dev/cpuctl/tasks
+ write /dev/cpuctl/cpu.rt_period_us 1000000
+ write /dev/cpuctl/cpu.rt_runtime_us 950000
+
# sets up initial cpusets for ActivityManager
# this ensures that the cpusets are present and usable, but the device's
# init.rc must actually set the correct cpus
@@ -329,6 +283,7 @@ on init
# system-background is for system tasks that should only run on
# little cores, not on bigs
+ # to be used only by init, so don't change system-bg permissions
mkdir /dev/cpuset/system-background
copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
copy /dev/cpuset/mems /dev/cpuset/system-background/mems
@@ -343,11 +298,6 @@ on init
copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
copy /dev/cpuset/mems /dev/cpuset/top-app/mems
- # create a cpuset for camera daemon processes
- mkdir /dev/cpuset/camera-daemon
- copy /dev/cpuset/cpus /dev/cpuset/camera-daemon/cpus
- copy /dev/cpuset/mems /dev/cpuset/camera-daemon/mems
-
# change permissions for all cpusets we'll touch at runtime
chown system system /dev/cpuset
chown system system /dev/cpuset/foreground
@@ -355,14 +305,12 @@ on init
chown system system /dev/cpuset/system-background
chown system system /dev/cpuset/top-app
chown system system /dev/cpuset/restricted
- chown system system /dev/cpuset/camera-daemon
chown system system /dev/cpuset/tasks
chown system system /dev/cpuset/foreground/tasks
chown system system /dev/cpuset/background/tasks
chown system system /dev/cpuset/system-background/tasks
chown system system /dev/cpuset/top-app/tasks
chown system system /dev/cpuset/restricted/tasks
- chown system system /dev/cpuset/camera-daemon/tasks
# set system-background to 0775 so SurfaceFlinger can touch it
chmod 0775 /dev/cpuset/system-background
@@ -373,7 +321,16 @@ on init
chmod 0664 /dev/cpuset/top-app/tasks
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
- chmod 0664 /dev/cpuset/camera-daemon/tasks
+
+ # freezer cgroup entries
+ mkdir /dev/freezer/frozen
+ write /dev/freezer/frozen/freezer.state FROZEN
+ chown system system /dev/freezer/cgroup.procs
+ chown system system /dev/freezer/frozen
+ chown system system /dev/freezer/frozen/freezer.state
+ chown system system /dev/freezer/frozen/cgroup.procs
+
+ chmod 0664 /dev/freezer/frozen/freezer.state
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
@@ -389,6 +346,8 @@ on init
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
+ chown root root /dev/cg2_bpf
+ chmod 0600 /dev/cg2_bpf
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
# Create location for fs_mgr to store abbreviated output from filesystem
@@ -512,10 +471,6 @@ on post-fs
# The bind+remount combination allows this to work in containers.
mount rootfs rootfs / remount bind ro nodev
- # Mount default storage into root namespace
- mount none /mnt/user/0 /storage bind rec
- mount none none /storage slave rec
-
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
restorecon --recursive --cross-filesystems /sys/kernel/debug
@@ -541,9 +496,6 @@ on post-fs
chown root log /proc/slabinfo
chmod 0440 /proc/slabinfo
- chown root log /proc/pagetypeinfo
- chmod 0440 /proc/pagetypeinfo
-
#change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks
chown root system /proc/kmsg
chmod 0440 /proc/kmsg
@@ -565,8 +517,6 @@ on post-fs
mkdir /metadata/bootstat 0750 system log
mkdir /metadata/ota 0700 root system
mkdir /metadata/ota/snapshots 0700 root system
- mkdir /metadata/userspacereboot 0770 root system
- mkdir /metadata/watchdog 0770 root system
mkdir /metadata/apex 0700 root system
mkdir /metadata/apex/sessions 0700 root system
@@ -587,20 +537,7 @@ on late-fs
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
- # Load trusted keys from dm-verity protected partitions
- exec -- /system/bin/fsverity_init --load-verified-keys
-
- # Set up a tracing instance for system_server to monitor error_report_end events.
- # These are sent by kernel tools like KASAN and KFENCE when a memory corruption
- # is detected.
- mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system
- restorecon_recursive /sys/kernel/tracing/instances/bootreceiver
- write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1
- write /sys/kernel/tracing/instances/bootreceiver/trace_options disable_on_free
- write /sys/kernel/tracing/instances/bootreceiver/events/error_report/error_report_end/enable 1
-
on post-fs-data
-
mark_post_data
# Start checkpoint before we touch data
@@ -620,60 +557,23 @@ on post-fs-data
mkdir /data/bootchart 0755 shell shell encryption=Require
bootchart start
- # Avoid predictable entropy pool. Carry over entropy from previous boot.
- copy /data/system/entropy.dat /dev/urandom
-
- mkdir /data/vendor 0771 root root encryption=Require
- mkdir /data/vendor_ce 0771 root root encryption=None
- mkdir /data/vendor_de 0771 root root encryption=None
- mkdir /data/vendor/hardware 0771 root root
-
- # Start tombstoned early to be able to store tombstones.
- mkdir /data/anr 0775 system system encryption=Require
- mkdir /data/tombstones 0771 system system encryption=Require
- mkdir /data/vendor/tombstones 0771 root root
- mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
- start tombstoned
-
# Make sure that apexd is started in the default namespace
enter_default_mount_ns
- # set up keystore directory structure first so that we can end early boot
- # and start apexd
- mkdir /data/misc 01771 system misc encryption=Require
- mkdir /data/misc/keystore 0700 keystore keystore
- # work around b/183668221
- restorecon /data/misc /data/misc/keystore
-
- # Boot level 30
- # odsign signing keys have MAX_BOOT_LEVEL=30
- # This is currently the earliest boot level, but we start at 30
- # to leave room for earlier levels.
- setprop keystore.boot_level 30
-
- # Now that /data is mounted and we have created /data/misc/keystore,
- # we can tell keystore to stop allowing use of early-boot keys,
- # and access its database for the first time to support creation and
- # use of MAX_BOOT_LEVEL keys.
- exec - system system -- /system/bin/vdc keymaster earlyBootEnded
-
# /data/apex is now available. Start apexd to scan and activate APEXes.
- #
- # To handle userspace reboots as well as devices that use FDE, make sure
- # that apexd is started cleanly here (set apexd.status="") and that it is
- # restarted if it's already running.
mkdir /data/apex 0755 root system encryption=None
mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
- mkdir /data/apex/decompressed 0755 root system encryption=Require
mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system
- mkdir /data/app-staging 0751 system system encryption=DeleteIfNecessary
- mkdir /data/apex/ota_reserved 0700 root system encryption=Require
- setprop apexd.status ""
- restart apexd
+ mkdir /data/app-staging 0750 system system encryption=None
+ start apexd
- # create rest of basic filesystem structure
+ # Avoid predictable entropy pool. Carry over entropy from previous boot.
+ copy /data/system/entropy.dat /dev/urandom
+
+ # create basic filesystem structure
+ mkdir /data/misc 01771 system misc encryption=Require
mkdir /data/misc/recovery 0770 system log
copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
@@ -694,9 +594,8 @@ on post-fs-data
chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf
mkdir /data/misc/bluetooth 0770 bluetooth bluetooth
mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth
- mkdir /data/misc/nfc 0770 nfc nfc
- mkdir /data/misc/nfc/logs 0770 nfc nfc
mkdir /data/misc/credstore 0700 credstore credstore
+ mkdir /data/misc/keystore 0700 keystore keystore
mkdir /data/misc/gatekeeper 0700 system system
mkdir /data/misc/keychain 0771 system system
mkdir /data/misc/net 0750 root shell
@@ -730,28 +629,26 @@ on post-fs-data
mkdir /data/misc/trace 0700 root root
# create location to store surface and window trace files
mkdir /data/misc/wmtrace 0700 system system
- # create location to store accessibility trace files
- mkdir /data/misc/a11ytrace 0700 system system
# profile file layout
mkdir /data/misc/profiles 0771 system system
mkdir /data/misc/profiles/cur 0771 system system
- mkdir /data/misc/profiles/ref 0771 system system
+ mkdir /data/misc/profiles/ref 0770 system system
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
mkdir /data/misc/installd 0700 root root
mkdir /data/misc/apexdata 0711 root root
mkdir /data/misc/apexrollback 0700 root root
- mkdir /data/misc/appcompat/ 0700 system system
mkdir /data/misc/snapshotctl_log 0755 root root
# create location to store pre-reboot information
mkdir /data/misc/prereboot 0700 system system
- # directory used for on-device refresh metrics file.
- mkdir /data/misc/odrefresh 0777 system system
- # directory used for on-device signing key blob
- mkdir /data/misc/odsign 0700 root root
mkdir /data/preloads 0775 system system encryption=None
+ mkdir /data/vendor 0771 root root encryption=Require
+ mkdir /data/vendor_ce 0771 root root encryption=None
+ mkdir /data/vendor_de 0771 root root encryption=None
+ mkdir /data/vendor/hardware 0771 root root
+
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
mkdir /data/local/tmp 0771 shell shell
@@ -763,20 +660,9 @@ on post-fs-data
mkdir /data/app-lib 0771 system system encryption=Require
mkdir /data/app 0771 system system encryption=Require
mkdir /data/property 0700 root root encryption=Require
-
- # create directory for updated font files.
- mkdir /data/fonts/ 0771 root root encryption=Require
- mkdir /data/fonts/files 0771 system system
- mkdir /data/fonts/config 0770 system system
-
- # Create directories to push tests to for each linker namespace.
- # Create the subdirectories in case the first test is run as root
- # so it doesn't end up owned by root.
- mkdir /data/local/tests 0700 shell shell
- mkdir /data/local/tests/product 0700 shell shell
- mkdir /data/local/tests/system 0700 shell shell
- mkdir /data/local/tests/unrestricted 0700 shell shell
- mkdir /data/local/tests/vendor 0700 shell shell
+ mkdir /data/tombstones 0771 system system encryption=Require
+ mkdir /data/vendor/tombstones 0771 root root
+ mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root encryption=Require
@@ -803,6 +689,8 @@ on post-fs-data
# the following directory.
mkdir /data/mediadrm 0770 mediadrm mediadrm encryption=Require
+ mkdir /data/anr 0775 system system encryption=Require
+
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc encryption=Require
mkdir /data/nfc/param 0770 nfc nfc
@@ -813,9 +701,6 @@ on post-fs-data
mkdir /data/ss 0700 system system encryption=Require
mkdir /data/system 0775 system system encryption=Require
- mkdir /data/system/environ 0700 system system
- # b/183861600 attempt to fix selinux label before running derive_classpath service
- restorecon /data/system/environ
mkdir /data/system/dropbox 0700 system system
mkdir /data/system/heapdump 0700 system system
mkdir /data/system/users 0775 system system
@@ -854,8 +739,6 @@ on post-fs-data
# Create mirror directory for jit profiles
mkdir /data_mirror/cur_profiles 0700 root root
mount none /data/misc/profiles/cur /data_mirror/cur_profiles bind rec
- mkdir /data_mirror/ref_profiles 0700 root root
- mount none /data/misc/profiles/ref /data_mirror/ref_profiles bind rec
mkdir /data/cache 0770 system cache encryption=Require
mkdir /data/cache/recovery 0770 system cache
@@ -865,7 +748,6 @@ on post-fs-data
# Delete these if need be, per b/139193659
mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
- mkdir /data/rollback-history 0700 system system encryption=DeleteIfNecessary
# Create root dir for Incremental Service
mkdir /data/incremental 0771 system system encryption=Require
@@ -890,37 +772,15 @@ on post-fs-data
init_user0
- # Set SELinux security contexts on upgrade or policy update.
- restorecon --recursive --skip-ce /data
-
- # Define and export *CLASSPATH variables
- # Must start before 'odsign', as odsign depends on *CLASSPATH variables
- exec_start derive_classpath
- load_exports /data/system/environ/classpath
-
- # Start the on-device signing daemon, and wait for it to finish, to ensure
- # ART artifacts are generated if needed.
- # Must start after 'derive_classpath' to have *CLASSPATH variables set.
- start odsign
-
- # Before we can lock keys and proceed to the next boot stage, wait for
- # odsign to be done with the key
- wait_for_prop odsign.key.done 1
-
- # Lock the fs-verity keyring, so no more keys can be added
- exec -- /system/bin/fsverity_init --lock
-
- # Bump the boot level to 1000000000; this prevents further on-device signing.
- # This is a special value that shuts down the thread which listens for
- # further updates.
- setprop keystore.boot_level 1000000000
-
# Allow apexd to snapshot and restore device encrypted apex data in the case
# of a rollback. This should be done immediately after DE_user data keys
# are loaded. APEXes should not access this data until this has been
# completed and apexd.status becomes "ready".
exec_start apexd-snapshotde
+ # Set SELinux security contexts on upgrade or policy update.
+ restorecon --recursive --skip-ce /data
+
# Check any timezone data in /data is newer than the copy in the time zone data
# module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
@@ -943,10 +803,25 @@ on post-fs-data
# Enable FUSE by default
setprop persist.sys.fuse true
+# Switch between sdcardfs and FUSE depending on persist property
+# TODO: Move this to ro property before launch because FDE devices
+# interact with persistent properties differently during boot
+on zygote-start && property:persist.sys.fuse=true
+ # Mount default storage into root namespace
+ mount none /mnt/user/0 /storage bind rec
+ mount none none /storage slave rec
+on zygote-start && property:persist.sys.fuse=false
+ # Mount default storage into root namespace
+ mount none /mnt/runtime/default /storage bind rec
+ mount none none /storage slave rec
+on zygote-start && property:persist.sys.fuse=""
+ # Mount default storage into root namespace
+ mount none /mnt/runtime/default /storage bind rec
+ mount none none /storage slave rec
+
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
- wait_for_prop odsign.verification.done 1
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
@@ -955,7 +830,6 @@ on zygote-start && property:ro.crypto.state=unencrypted
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
- wait_for_prop odsign.verification.done 1
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
@@ -964,7 +838,6 @@ on zygote-start && property:ro.crypto.state=unsupported
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- wait_for_prop odsign.verification.done 1
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
@@ -972,11 +845,6 @@ on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type
start zygote
start zygote_secondary
-on boot && property:ro.config.low_ram=true
- # Tweak background writeout
- write /proc/sys/vm/dirty_expire_centisecs 200
- write /proc/sys/vm/dirty_background_ratio 5
-
on boot
# basic network init
ifup lo
@@ -998,26 +866,22 @@ on boot
chown root system /sys/block/zram0/writeback
chmod 0664 /sys/block/zram0/writeback
- # to access F2FS sysfs on dm-<num> directly
- mkdir /dev/sys/fs/by-name 0755 system system
- symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
-
- # to access dm-<num> sysfs
- mkdir /dev/sys/block/by-name 0755 system system
- symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
+ # Tweak background writeout
+ write /proc/sys/vm/dirty_expire_centisecs 200
+ write /proc/sys/vm/dirty_background_ratio 5
- # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
+ # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs
# to avoid power consumption when system becomes mostly idle. Be careful
# to make it too large, since it may bring userdata loss, if they
# are not aware of using fsync()/sync() to prepare sudden power-cut.
- write /dev/sys/fs/by-name/userdata/cp_interval 200
- write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
- write /dev/sys/fs/by-name/userdata/iostat_enable 1
+ write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+ write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
+ write /sys/fs/f2fs/${dev.mnt.blk.data}/iostat_enable 1
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
# Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
- write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
+ write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
@@ -1076,13 +940,16 @@ on boot
chown root radio /proc/cmdline
# Define default initial receive window size in segments.
- setprop net.tcp_def_init_rwnd 60
+ setprop net.tcp.default_init_rwnd 60
# Start standard binderized HAL daemons
class_start hal
class_start core
+ # Requires keystore (currently a core service) to be ready first.
+ exec -- /system/bin/fsverity_init
+
on nonencrypted
class_start main
class_start late_start
@@ -1115,7 +982,6 @@ on property:vold.decrypt=trigger_restart_framework
class_start main
class_start late_start
setprop service.bootanim.exit 0
- setprop service.bootanim.progress 0
start bootanim
on property:vold.decrypt=trigger_shutdown_framework
@@ -1136,14 +1002,9 @@ on property:sys.boot_completed=1
on property:sys.sysctl.extra_free_kbytes=*
write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
-# Allow users to drop caches
-on property:perf.drop_caches=3
- write /proc/sys/vm/drop_caches 3
- setprop perf.drop_caches 0
-
# "tcp_default_init_rwnd" Is too long!
-on property:net.tcp_def_init_rwnd=*
- write /proc/sys/net/ipv4/tcp_default_init_rwnd ${net.tcp_def_init_rwnd}
+on property:sys.sysctl.tcp_def_init_rwnd=*
+ write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
# perf_event_open syscall security:
# Newer kernels have the ability to control the use of the syscall via SELinux
@@ -1172,14 +1033,6 @@ on property:security.perf_harden=1
write /proc/sys/kernel/perf_cpu_time_max_percent 25
write /proc/sys/kernel/perf_event_mlock_kb 516
-# This property can be set only on userdebug/eng. See neverallow rule in
-# /system/sepolicy/private/property.te .
-on property:security.lower_kptr_restrict=1
- write /proc/sys/kernel/kptr_restrict 0
-
-on property:security.lower_kptr_restrict=0
- write /proc/sys/kernel/kptr_restrict 2
-
# on shutdown
# In device's init.rc, this trigger can be used to do device-specific actions
@@ -1208,8 +1061,6 @@ on property:ro.debuggable=1
chmod 0773 /data/misc/trace
# Give reads to anyone for the window trace folder on debug builds.
chmod 0775 /data/misc/wmtrace
- # Give reads to anyone for the accessibility trace folder on debug builds.
- chmod 0775 /data/misc/a11ytrace
on init && property:ro.debuggable=1
start console
@@ -1220,10 +1071,10 @@ on userspace-reboot-requested
setprop dev.bootcomplete ""
setprop sys.init.updatable_crashing ""
setprop sys.init.updatable_crashing_process_name ""
+ setprop apexd.status ""
setprop sys.user.0.ce_available ""
setprop sys.shutdown.requested ""
setprop service.bootanim.exit ""
- setprop service.bootanim.progress ""
on userspace-reboot-fs-remount
# Make sure that vold is running.
@@ -1237,7 +1088,6 @@ on userspace-reboot-fs-remount
umount /data_mirror/data_ce/null
umount /data_mirror/data_de/null
umount /data_mirror/cur_profiles
- umount /data_mirror/ref_profiles
umount /data_mirror
remount_userdata
start bootanim
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 0730ccecf..27b05ec17 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -19,9 +19,6 @@ service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
updatable
seclabel u:r:adbd:s0
-on property:vendor.sys.usb.adb.disabled=*
- setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}
-
# Set default value on sys.usb.configfs early in boot sequence. It will be
# overridden in `on boot` action of init.hardware.rc.
on init
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 9469a4873..e827cf557 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,4 +13,3 @@ service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
- critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
new file mode 100644
index 000000000..fe6dcfa47
--- /dev/null
+++ b/rootdir/init.zygote32_64.rc
@@ -0,0 +1,25 @@
+service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
+ class main
+ priority -20
+ user root
+ group root readproc reserved_disk
+ socket zygote stream 660 root system
+ socket usap_pool_primary stream 660 root system
+ onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
+ onrestart write /sys/power/state on
+ onrestart restart audioserver
+ onrestart restart cameraserver
+ onrestart restart media
+ onrestart restart netd
+ onrestart restart wificond
+ writepid /dev/cpuset/foreground/tasks
+
+service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+ class main
+ priority -20
+ user root
+ group root readproc reserved_disk
+ socket zygote_secondary stream 660 root system
+ socket usap_pool_secondary stream 660 root system
+ onrestart restart zygote
+ writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 98dc0881f..adc703102 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,4 +13,3 @@ service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-s
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
- critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 3eee180ba..fb9e99b69 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -13,7 +13,6 @@ service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-s
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
- critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 56e774bdc..9c2cdf27f 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,6 +1,3 @@
-import /vendor/etc/ueventd.rc
-import /odm/etc/ueventd.rc
-
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
uevent_socket_rcvbuf_size 16M
@@ -20,9 +17,6 @@ subsystem sound
devname uevent_devpath
dirname /dev/snd
-subsystem dma_heap
- devname uevent_devpath
- dirname /dev/dma_heap
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
#
@@ -37,15 +31,14 @@ subsystem dma_heap
/dev/tty 0666 root root
/dev/random 0666 root root
/dev/urandom 0666 root root
+# Make HW RNG readable by group system to let EntropyMixer read it.
+/dev/hw_random 0440 root system
/dev/ashmem* 0666 root root
/dev/binder 0666 root root
/dev/hwbinder 0666 root root
/dev/vndbinder 0666 root root
/dev/pmsg0 0222 root log
-/dev/dma_heap/system 0444 system system
-/dev/dma_heap/system-uncached 0444 system system
-/dev/dma_heap/system-secure 0444 system system
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
@@ -67,10 +60,6 @@ subsystem dma_heap
# CDMA radio interface MUX
/dev/ppp 0660 radio vpn
-# Virtualisation is managed by Virt Manager
-/dev/kvm 0600 virtmanager root
-/dev/vhost-vsock 0600 virtmanager root
-
# sysfs properties
/sys/devices/platform/trusty.* trusty_version 0440 root log
/sys/devices/virtual/input/input* enable 0660 root input
@@ -78,5 +67,3 @@ subsystem dma_heap
/sys/devices/virtual/usb_composite/* enable 0664 root system
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system
-/sys/devices/virtual/misc/uhid/*/leds/* brightness 0664 system system
-/sys/devices/virtual/misc/uhid/*/leds/* multi_intensity 0664 system system
diff --git a/run-as/Android.bp b/run-as/Android.bp
index 9baee8f11..840a43c47 100644
--- a/run-as/Android.bp
+++ b/run-as/Android.bp
@@ -14,23 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["system_core_run-as_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_core_run-as_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_binary {
name: "run-as",
srcs: [
@@ -42,5 +25,4 @@ cc_binary {
"libpackagelistparser",
"libminijail",
],
- header_libs: ["libcutils_headers"],
}
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index cc92c68d0..432c434b4 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -247,12 +247,12 @@ int main(int argc, char* argv[]) {
std::string seinfo = std::string(info.seinfo) + ":fromRunAs";
if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {
- error(1, errno, "couldn't set SELinux security context '%s'", seinfo.c_str());
+ error(1, errno, "couldn't set SELinux security context");
}
// cd into the data directory, and set $HOME correspondingly.
if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) {
- error(1, errno, "couldn't chdir to package's data directory '%s'", info.data_dir);
+ error(1, errno, "couldn't chdir to package's data directory");
}
setenv("HOME", info.data_dir, 1);
diff --git a/sdcard/Android.bp b/sdcard/Android.bp
index 10a7d16c3..c096587ba 100644
--- a/sdcard/Android.bp
+++ b/sdcard/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
srcs: ["sdcard.cpp"],
name: "sdcard",
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index 2f0cb102b..da112c906 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -1,9 +1,5 @@
// Copyright 2019 The Android Open Source Project
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "set-verity-state",
srcs: ["set-verity-state.cpp"],
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index b7d7490dc..b5a5fb6ce 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
phony {
name: "shell_and_utilities",
required: [
@@ -40,7 +36,7 @@ phony {
"sh.recovery",
"toolbox.recovery",
"toybox.recovery",
- "ziptool.recovery",
+ "unzip.recovery",
],
}
@@ -55,13 +51,3 @@ phony {
"toybox_vendor",
],
}
-
-// shell and utilities for first stage console. The list of binaries are
-// enough for debugging purposes.
-phony {
- name: "shell_and_utilities_vendor_ramdisk",
- required: [
- "sh.vendor_ramdisk",
- "toybox.vendor_ramdisk",
- ],
-}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index f339553f0..3bee87507 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -26,8 +26,7 @@ each release starting with Gingerbread. This doesn't tell the full story,
because the toolbox implementations did have bugs fixed and options added
over the years. Gingerbread's rm, for example, supported `-r`/`-R` but not
`-f`. But this gives you an idea of what was available in any given release,
-and how usable it was likely to be. (**Bold** marks where we switched to toybox
-or first added something to toybox.)
+and how usable it was likely to be.
Also note that in any given release `toybox` probably contains more
commands than there are symlinks for in `/system/bin`. You can get the
@@ -99,7 +98,7 @@ BSD: dd du grep
toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
env expand expr fallocate false find free getenforce getprop groups
head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
@@ -119,18 +118,18 @@ BSD: dd grep
toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
sendevent start stop top
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
-chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
-dos2unix **du** echo env expand expr fallocate false find **flock** free
+toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
+dos2unix du echo env expand expr fallocate false find flock free
getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
-lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
+insmod ionice iorenice kill killall load\_policy ln logname losetup ls
+lsmod lsof lsusb md5sum mkdir mknod mkswap mktemp modinfo more mount
mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
-pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
+pmap printenv printf pwd readlink realpath renice restorecon rm rmdir
rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
-**uptime** usleep vmstat wc which whoami xargs **xxd** yes
+time timeout touch tr true truncate tty ulimit umount uname uniq unix2dos
+uptime usleep vmstat wc which whoami xargs xxd yes
## Android 8.0 (Oreo)
@@ -141,20 +140,20 @@ bzip2: bzcat bzip2 bunzip2
toolbox: getevent newfs\_msdos
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
-dos2unix du echo env expand expr fallocate false **file** find flock free
-getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
-losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
-mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
-nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
-readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
-seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
-**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
-sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
-tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
-vmstat wc which whoami xargs xxd yes **zcat**
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
+seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout top touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
+vmstat wc which whoami xargs xxd yes zcat
## Android 9.0 (Pie)
@@ -167,9 +166,9 @@ one-true-awk: awk
toolbox: getevent getprop newfs\_msdos
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
-dos2unix du echo env expand expr fallocate false file find flock **fmt** free
+dos2unix du echo env expand expr fallocate false file find flock fmt free
getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
@@ -177,13 +176,13 @@ modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
+sort split start stat stop strings stty swapoff swapon sync sysctl tac
tail tar taskset tee time timeout top touch tr true truncate tty ulimit
umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
which whoami xargs xxd yes zcat
-## Android 10 ("Q")
+## Android Q
BSD: grep fsck\_msdos newfs\_msdos
@@ -193,27 +192,27 @@ one-true-awk: awk
toolbox: getevent getprop
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
-false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
-getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
-**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
-**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
-losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
-mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
-paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
-printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
-**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
sort split start stat stop strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
-true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
-**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
+tac tail tar taskset tee time timeout top touch tr traceroute traceroute6
+true truncate tty tunctl ulimit umount uname uniq unix2dos unlink
+unshare uptime usleep uudecode uuencode uuidgen vconfig vmstat watch
wc which whoami xargs xxd yes zcat
-## Android 11 ("R")
+## Android R
BSD: fsck\_msdos newfs\_msdos
@@ -225,54 +224,22 @@ one-true-awk: awk
toolbox: getevent getprop setprop start stop
-toybox (0.8.3-ish): acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
-chown chroot chrt cksum clear cmp comm cp cpio cut date dd **devmem**
+toybox: acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem
df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
-false fgrep file find flock fmt free freeramdisk fsfreeze **fsync** getconf
-getenforce getfattr **getopt** grep groups gunzip gzip head help hostname
+false fgrep file find flock fmt free freeramdisk fsfreeze fsync getconf
+getenforce getfattr getopt grep groups gunzip gzip head help hostname
hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd
insmod install ionice iorenice iotop kill killall ln load\_policy log
logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
-printf prlimit ps pwd pwdx **readelf** readlink realpath renice restorecon
+printf prlimit ps pwd pwdx readelf readlink realpath renice restorecon
rev rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort
split stat strings stty swapoff swapon sync sysctl tac tail tar taskset
tee time timeout top touch tr traceroute traceroute6 true truncate
tty tunctl ulimit umount uname uniq unix2dos unlink unshare uptime
-usleep uudecode uuencode uuidgen vconfig **vi** vmstat watch wc which
+usleep uudecode uuencode uuidgen vconfig vi vmstat watch wc which
whoami xargs xxd yes zcat
-
-## Android ("S")
-
-BSD: fsck\_msdos newfs\_msdos
-
-bzip2: bzcat bzip2 bunzip2
-
-gavinhoward/bc: bc
-
-one-true-awk: awk
-
-toolbox: getevent getprop setprop start stop
-
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
-chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
-dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
-expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
-fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
-help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
-inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
-log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
-microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
-mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
-od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
-printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
-restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
-seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
-sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee **test** time timeout top touch tr traceroute
-traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
-unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
-vmstat watch wc which whoami xargs xxd yes zcat
diff --git a/storaged/Android.bp b/storaged/Android.bp
index ec27a08cb..cc19481bc 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_defaults {
name: "storaged_defaults",
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 8594ec403..4ca5f5a0e 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,20 +1,3 @@
-package {
- default_applicable_licenses: ["system_core_toolbox_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "system_core_toolbox_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_defaults {
name: "toolbox_defaults",
cflags: [
@@ -36,6 +19,7 @@ genrule {
cc_defaults {
name: "toolbox_binary_defaults",
defaults: ["toolbox_defaults"],
+ cpp_std: "experimental",
srcs: [
"toolbox.c",
"getevent.c",
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
index 5e2c5813f..682a067b3 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1,2 +1 @@
-include platform/system/core:/janitors/OWNERS
-per-file modprobe.c=willmcvicker@google.com,dvander@google.com
+enh@google.com
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 711586a98..3ffa74ecd 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -17,16 +17,11 @@
#include <ctype.h>
#include <getopt.h>
#include <stdlib.h>
+#include <iostream>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
#include <android-base/strings.h>
#include <modprobe/modprobe.h>
-namespace {
-
enum modprobe_mode {
AddModulesMode,
RemoveModulesMode,
@@ -34,104 +29,47 @@ enum modprobe_mode {
ShowDependenciesMode,
};
-void print_usage(void) {
- LOG(INFO) << "Usage:";
- LOG(INFO);
- // -d option is required on Android
- LOG(INFO) << " modprobe [options] -d DIR [--all=FILE|MODULE]...";
- LOG(INFO) << " modprobe [options] -d DIR MODULE [symbol=value]...";
- LOG(INFO);
- LOG(INFO) << "Options:";
- LOG(INFO) << " --all=FILE: FILE to acquire module names from";
- LOG(INFO) << " -b, --use-blocklist: Apply blocklist to module names too";
- LOG(INFO) << " -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
- LOG(INFO) << " -D, --show-depends: Print dependencies for modules only, do not load";
- LOG(INFO) << " -h, --help: Print this help";
- LOG(INFO) << " -l, --list: List modules matching pattern";
- LOG(INFO) << " -r, --remove: Remove MODULE (multiple modules may be specified)";
- LOG(INFO) << " -s, --syslog: print to syslog also";
- LOG(INFO) << " -q, --quiet: disable messages";
- LOG(INFO) << " -v, --verbose: enable more messages, even more with a second -v";
- LOG(INFO);
-}
-
-#define check_mode() \
- if (mode != AddModulesMode) { \
- LOG(ERROR) << "multiple mode flags specified"; \
- print_usage(); \
- return EXIT_FAILURE; \
- }
-
-std::string stripComments(const std::string& str) {
- for (std::string rv = str;;) {
- auto comment = rv.find('#');
- if (comment == std::string::npos) return rv;
- auto end = rv.find('\n', comment);
- if (end != std::string::npos) end = end - comment;
- rv.erase(comment, end);
- }
- /* NOTREACHED */
+static void print_usage(void) {
+ std::cerr << "Usage:" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << " modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
+ std::cerr << " modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Options:" << std::endl;
+ std::cerr << " -b: Apply blocklist to module names too" << std::endl;
+ std::cerr << " -d: Load modules from DIR, option may be used multiple times" << std::endl;
+ std::cerr << " -D: Print dependencies for modules only, do not load";
+ std::cerr << " -h: Print this help" << std::endl;
+ std::cerr << " -l: List modules matching pattern" << std::endl;
+ std::cerr << " -r: Remove MODULE (multiple modules may be specified)" << std::endl;
+ std::cerr << " -q: Quiet" << std::endl;
+ std::cerr << " -v: Verbose" << std::endl;
+ std::cerr << std::endl;
}
-auto syslog = false;
-
-void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
- const char* file, unsigned int line, const char* message) {
- android::base::StdioLogger(id, severity, tag, file, line, message);
- if (syslog && message[0]) {
- android::base::KernelLogger(id, severity, tag, file, line, message);
+#define check_mode() \
+ if (mode != AddModulesMode) { \
+ std::cerr << "Error, multiple mode flags specified" << std::endl; \
+ print_usage(); \
+ return EXIT_FAILURE; \
}
-}
-
-} // anonymous namespace
extern "C" int modprobe_main(int argc, char** argv) {
- android::base::InitLogging(argv, MyLogger);
- android::base::SetMinimumLogSeverity(android::base::INFO);
-
std::vector<std::string> modules;
std::string module_parameters;
- std::string mods;
std::vector<std::string> mod_dirs;
modprobe_mode mode = AddModulesMode;
bool blocklist = false;
+ bool verbose = false;
int rv = EXIT_SUCCESS;
int opt;
- int option_index = 0;
- // NB: We have non-standard short options -l and -D to make it easier for
- // OEMs to transition from toybox.
- // clang-format off
- static struct option long_options[] = {
- { "all", optional_argument, 0, 'a' },
- { "use-blocklist", no_argument, 0, 'b' },
- { "dirname", required_argument, 0, 'd' },
- { "show-depends", no_argument, 0, 'D' },
- { "help", no_argument, 0, 'h' },
- { "list", no_argument, 0, 'l' },
- { "quiet", no_argument, 0, 'q' },
- { "remove", no_argument, 0, 'r' },
- { "syslog", no_argument, 0, 's' },
- { "verbose", no_argument, 0, 'v' },
- };
- // clang-format on
- while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
+ while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
switch (opt) {
case 'a':
// toybox modprobe supported -a to load multiple modules, this
- // is supported here by default, ignore flag if no argument.
+ // is supported here by default, ignore flag
check_mode();
- if (optarg == NULL) break;
- if (!android::base::ReadFileToString(optarg, &mods)) {
- PLOG(ERROR) << "Failed to open " << optarg;
- rv = EXIT_FAILURE;
- }
- for (auto mod : android::base::Split(stripComments(mods), "\n")) {
- mod = android::base::Trim(mod);
- if (mod == "") continue;
- if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
- modules.emplace_back(mod);
- }
break;
case 'b':
blocklist = true;
@@ -144,33 +82,24 @@ extern "C" int modprobe_main(int argc, char** argv) {
mode = ShowDependenciesMode;
break;
case 'h':
- android::base::SetMinimumLogSeverity(android::base::INFO);
print_usage();
- return rv;
+ return EXIT_SUCCESS;
case 'l':
check_mode();
mode = ListModulesMode;
break;
case 'q':
- android::base::SetMinimumLogSeverity(android::base::WARNING);
+ verbose = false;
break;
case 'r':
check_mode();
mode = RemoveModulesMode;
break;
- case 's':
- syslog = true;
- break;
case 'v':
- if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
- android::base::SetMinimumLogSeverity(android::base::VERBOSE);
- } else {
- android::base::SetMinimumLogSeverity(android::base::DEBUG);
- }
+ verbose = true;
break;
default:
- LOG(ERROR) << "Unrecognized option: " << opt;
- print_usage();
+ std::cerr << "Unrecognized option: " << opt << std::endl;
return EXIT_FAILURE;
}
}
@@ -189,51 +118,60 @@ extern "C" int modprobe_main(int argc, char** argv) {
}
}
- LOG(DEBUG) << "mode is " << mode;
- LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
- LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
- LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
+ if (verbose) {
+ std::cout << "mode is " << mode << std::endl;
+ std::cout << "verbose is " << verbose << std::endl;
+ std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
+ std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
+ std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
+ << std::endl;
+ }
if (modules.empty()) {
if (mode == ListModulesMode) {
// emulate toybox modprobe list with no pattern (list all)
modules.emplace_back("*");
} else {
- LOG(ERROR) << "No modules given.";
+ std::cerr << "No modules given." << std::endl;
print_usage();
return EXIT_FAILURE;
}
}
if (mod_dirs.empty()) {
- LOG(ERROR) << "No module configuration directories given.";
+ std::cerr << "No module configuration directories given." << std::endl;
print_usage();
return EXIT_FAILURE;
}
if (parameter_count && modules.size() > 1) {
- LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
+ std::cerr << "Only one module may be loaded when specifying module parameters."
+ << std::endl;
print_usage();
return EXIT_FAILURE;
}
- Modprobe m(mod_dirs, "modules.load", blocklist);
+ Modprobe m(mod_dirs);
+ m.EnableVerbose(verbose);
+ if (blocklist) {
+ m.EnableBlocklist(true);
+ }
for (const auto& module : modules) {
switch (mode) {
case AddModulesMode:
if (!m.LoadWithAliases(module, true, module_parameters)) {
- PLOG(ERROR) << "Failed to load module " << module;
+ std::cerr << "Failed to load module " << module;
rv = EXIT_FAILURE;
}
break;
case RemoveModulesMode:
if (!m.Remove(module)) {
- PLOG(ERROR) << "Failed to remove module " << module;
+ std::cerr << "Failed to remove module " << module;
rv = EXIT_FAILURE;
}
break;
case ListModulesMode: {
std::vector<std::string> list = m.ListModules(module);
- LOG(INFO) << android::base::Join(list, "\n");
+ std::cout << android::base::Join(list, "\n") << std::endl;
break;
}
case ShowDependenciesMode: {
@@ -244,17 +182,17 @@ extern "C" int modprobe_main(int argc, char** argv) {
rv = EXIT_FAILURE;
break;
}
- LOG(INFO) << "Dependencies for " << module << ":";
- LOG(INFO) << "Soft pre-dependencies:";
- LOG(INFO) << android::base::Join(pre_deps, "\n");
- LOG(INFO) << "Hard dependencies:";
- LOG(INFO) << android::base::Join(deps, "\n");
- LOG(INFO) << "Soft post-dependencies:";
- LOG(INFO) << android::base::Join(post_deps, "\n");
+ std::cout << "Dependencies for " << module << ":" << std::endl;
+ std::cout << "Soft pre-dependencies:" << std::endl;
+ std::cout << android::base::Join(pre_deps, "\n") << std::endl;
+ std::cout << "Hard dependencies:" << std::endl;
+ std::cout << android::base::Join(deps, "\n") << std::endl;
+ std::cout << "Soft post-dependencies:" << std::endl;
+ std::cout << android::base::Join(post_deps, "\n") << std::endl;
break;
}
default:
- LOG(ERROR) << "Bad mode";
+ std::cerr << "Bad mode";
rv = EXIT_FAILURE;
}
}
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
index 46314cfcf..cffb89cc5 100644
--- a/toolbox/start.cpp
+++ b/toolbox/start.cpp
@@ -37,6 +37,7 @@ static void ControlService(bool start, const std::string& service) {
static void ControlDefaultServices(bool start) {
std::vector<std::string> services = {
+ "iorapd",
"netd",
"surfaceflinger",
"audioserver",
@@ -91,4 +92,4 @@ extern "C" int start_main(int argc, char** argv) {
extern "C" int stop_main(int argc, char** argv) {
return StartStop(argc, argv, false);
-}
+} \ No newline at end of file
diff --git a/trusty/Android.bp b/trusty/Android.bp
deleted file mode 100644
index 38c6204db..000000000
--- a/trusty/Android.bp
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["system_core_trusty_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "system_core_trusty_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-MIT",
- ],
- // large-scale-change unable to identify any license_text files
-}
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 5c4e03ae9..1fb473eb0 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,7 +1,7 @@
-armellel@google.com
-arve@android.com
+arve@google.com
+dkrahn@google.com
+drewry@google.com
gmar@google.com
-marcone@google.com
mmaurer@google.com
ncbray@google.com
swillden@google.com
diff --git a/trusty/apploader/Android.bp b/trusty/apploader/Android.bp
deleted file mode 100644
index 4411964ad..000000000
--- a/trusty/apploader/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open-Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "trusty_apploader",
- vendor: true,
-
- srcs: [
- "apploader.cpp",
- ],
-
- shared_libs: [
- "libbase",
- "libc",
- "liblog",
- "libtrusty",
- "libdmabufheap",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
deleted file mode 100644
index 4aca375fa..000000000
--- a/trusty/apploader/apploader.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "TrustyAppLoader"
-
-#include <BufferAllocator/BufferAllocator.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/sendfile.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <trusty/tipc.h>
-#include <unistd.h>
-#include <algorithm>
-#include <string>
-
-#include "apploader_ipc.h"
-
-using android::base::unique_fd;
-using std::string;
-
-constexpr const char kTrustyDefaultDeviceName[] = "/dev/trusty-ipc-dev0";
-
-static const char* dev_name = kTrustyDefaultDeviceName;
-
-static const char* _sopts = "hD:";
-static const struct option _lopts[] = {
- {"help", no_argument, 0, 'h'},
- {"dev", required_argument, 0, 'D'},
- {0, 0, 0, 0},
-};
-
-static const char* usage =
- "Usage: %s [options] package-file\n"
- "\n"
- "options:\n"
- " -h, --help prints this message and exit\n"
- " -D, --dev name Trusty device name\n"
- "\n";
-
-static void print_usage_and_exit(const char* prog, int code) {
- fprintf(stderr, usage, prog);
- exit(code);
-}
-
-static void parse_options(int argc, char** argv) {
- int c;
- int oidx = 0;
-
- while (1) {
- c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
- if (c == -1) {
- break; /* done */
- }
-
- switch (c) {
- case 'h':
- print_usage_and_exit(argv[0], EXIT_SUCCESS);
- break;
-
- case 'D':
- dev_name = strdup(optarg);
- break;
-
- default:
- print_usage_and_exit(argv[0], EXIT_FAILURE);
- }
- }
-}
-
-static unique_fd read_file(const char* file_name, off64_t* out_file_size) {
- int rc;
- long page_size = sysconf(_SC_PAGESIZE);
- off64_t file_size, file_page_offset, file_page_size;
- struct stat64 st;
-
- unique_fd file_fd(TEMP_FAILURE_RETRY(open(file_name, O_RDONLY)));
- if (!file_fd.ok()) {
- PLOG(ERROR) << "Error opening file " << file_name;
- return {};
- }
-
- rc = fstat64(file_fd, &st);
- if (rc < 0) {
- PLOG(ERROR) << "Error calling stat on file '" << file_name << "'";
- return {};
- }
-
- assert(st.st_size >= 0);
- file_size = st.st_size;
-
- /* The dmabuf size needs to be a multiple of the page size */
- file_page_offset = file_size & (page_size - 1);
- if (file_page_offset) {
- file_page_offset = page_size - file_page_offset;
- }
- if (__builtin_add_overflow(file_size, file_page_offset, &file_page_size)) {
- LOG(ERROR) << "Failed to page-align file size";
- return {};
- }
-
- BufferAllocator alloc;
- unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));
- if (!dmabuf_fd.ok()) {
- LOG(ERROR) << "Error creating dmabuf: " << dmabuf_fd.get();
- return dmabuf_fd;
- }
-
- void* shm = mmap(0, file_page_size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd, 0);
- if (shm == MAP_FAILED) {
- return {};
- }
-
- off64_t file_offset = 0;
- while (file_offset < file_size) {
- ssize_t num_read = TEMP_FAILURE_RETRY(
- pread(file_fd, (char*)shm + file_offset, file_size - file_offset, file_offset));
-
- if (num_read < 0) {
- PLOG(ERROR) << "Error reading package file '" << file_name << "'";
- break;
- }
-
- if (num_read == 0) {
- LOG(ERROR) << "Unexpected end of file '" << file_name << "'";
- break;
- }
-
- file_offset += (off64_t)num_read;
- }
-
- munmap(shm, file_page_size);
-
- if (file_offset < file_size) {
- return {};
- }
-
- assert(file_offset == file_size);
- if (out_file_size) {
- *out_file_size = file_size;
- }
-
- return dmabuf_fd;
-}
-
-static ssize_t send_load_message(int tipc_fd, int package_fd, off64_t package_size) {
- struct apploader_header hdr = {
- .cmd = APPLOADER_CMD_LOAD_APPLICATION,
- };
- struct apploader_load_app_req req = {
- .package_size = static_cast<uint64_t>(package_size),
- };
- struct iovec tx[2] = {{&hdr, sizeof(hdr)}, {&req, sizeof(req)}};
- struct trusty_shm shm = {
- .fd = package_fd,
- .transfer = TRUSTY_SHARE,
- };
- return tipc_send(tipc_fd, tx, 2, &shm, 1);
-}
-
-static ssize_t read_response(int tipc_fd) {
- struct apploader_resp resp;
- ssize_t rc = read(tipc_fd, &resp, sizeof(resp));
- if (rc < 0) {
- PLOG(ERROR) << "Failed to read response";
- return rc;
- }
-
- if (rc < sizeof(resp)) {
- LOG(ERROR) << "Not enough data in response: " << rc;
- return -EIO;
- }
-
- if (resp.hdr.cmd != (APPLOADER_CMD_LOAD_APPLICATION | APPLOADER_RESP_BIT)) {
- LOG(ERROR) << "Invalid command in response: " << resp.hdr.cmd;
- return -EINVAL;
- }
-
- switch (resp.error) {
- case APPLOADER_NO_ERROR:
- break;
- case APPLOADER_ERR_UNKNOWN_CMD:
- LOG(ERROR) << "Error: unknown command";
- break;
- case APPLOADER_ERR_INVALID_CMD:
- LOG(ERROR) << "Error: invalid command arguments";
- break;
- case APPLOADER_ERR_NO_MEMORY:
- LOG(ERROR) << "Error: out of Trusty memory";
- break;
- case APPLOADER_ERR_VERIFICATION_FAILED:
- LOG(ERROR) << "Error: failed to verify the package";
- break;
- case APPLOADER_ERR_LOADING_FAILED:
- LOG(ERROR) << "Error: failed to load the package";
- break;
- case APPLOADER_ERR_ALREADY_EXISTS:
- LOG(ERROR) << "Error: application already exists";
- break;
- case APPLOADER_ERR_INTERNAL:
- LOG(ERROR) << "Error: internal apploader error";
- break;
- default:
- LOG(ERROR) << "Unrecognized error: " << resp.error;
- break;
- }
-
- return static_cast<ssize_t>(resp.error);
-}
-
-static ssize_t send_app_package(const char* package_file_name) {
- ssize_t rc = 0;
- int tipc_fd = -1;
- off64_t package_size;
-
- unique_fd package_fd = read_file(package_file_name, &package_size);
- if (!package_fd.ok()) {
- rc = -1;
- goto err_read_file;
- }
-
- tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);
- if (tipc_fd < 0) {
- LOG(ERROR) << "Failed to connect to Trusty app loader: " << strerror(-tipc_fd);
- rc = tipc_fd;
- goto err_tipc_connect;
- }
-
- rc = send_load_message(tipc_fd, package_fd, package_size);
- if (rc < 0) {
- LOG(ERROR) << "Failed to send package: " << rc;
- goto err_send;
- }
-
- rc = read_response(tipc_fd);
-
-err_send:
- tipc_close(tipc_fd);
-err_tipc_connect:
-err_read_file:
- return rc;
-}
-
-int main(int argc, char** argv) {
- parse_options(argc, argv);
- if (optind + 1 != argc) {
- print_usage_and_exit(argv[0], EXIT_FAILURE);
- }
-
- int rc = send_app_package(argv[optind]);
- return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
deleted file mode 100644
index d8c915eed..000000000
--- a/trusty/apploader/apploader_ipc.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#define APPLOADER_PORT "com.android.trusty.apploader"
-
-enum apploader_command : uint32_t {
- APPLOADER_REQ_SHIFT = 1,
- APPLOADER_RESP_BIT = 1,
-
- APPLOADER_CMD_LOAD_APPLICATION = (0 << APPLOADER_REQ_SHIFT),
- APPLOADER_CMD_GET_VERSION = (1 << APPLOADER_REQ_SHIFT),
- APPLOADER_CMD_UNLOAD_APPLICATION = (2 << APPLOADER_REQ_SHIFT),
-};
-
-/**
- * enum apploader_error - error codes for apploader
- * @APPLOADER_NO_ERROR: no error
- * @APPLOADER_ERR_UNKNOWN_CMD: unknown or not implemented command
- * @APPLOADER_ERR_INVALID_CMD: invalid arguments or inputs passed to
- * command
- * @APPLOADER_ERR_NO_MEMORY: failed to allocate memory
- * @APPLOADER_ERR_VERIFICATION_FAILED: failed to verify input application
- * package for any reason, e.g., signature
- * verification failed
- * @APPLOADER_ERR_LOADING_FAILED: Trusty kernel or apploader service
- * failed to load application
- * @APPLOADER_ERR_ALREADY_EXISTS: application has already been loaded
- * @APPLOADER_ERR_INTERNAL: miscellaneous or internal apploader
- * error not covered by the above
- */
-enum apploader_error : uint32_t {
- APPLOADER_NO_ERROR = 0,
- APPLOADER_ERR_UNKNOWN_CMD,
- APPLOADER_ERR_INVALID_CMD,
- APPLOADER_ERR_NO_MEMORY,
- APPLOADER_ERR_VERIFICATION_FAILED,
- APPLOADER_ERR_LOADING_FAILED,
- APPLOADER_ERR_ALREADY_EXISTS,
- APPLOADER_ERR_INTERNAL,
-};
-
-/**
- * apploader_header - Serial header for communicating with apploader
- * @cmd: the command; one of &enum apploader_command values.
- */
-struct apploader_header {
- uint32_t cmd;
-} __packed;
-
-/**
- * apploader_load_app_req - Serial arguments for LOAD_APPLICATION command
- * @package_size: size of the application package.
- *
- * Load an application from a given memory region. The request message also
- * contains a handle for a memfd that contains the application package.
- *
- * The response is a &struct apploader_resp with the error code or
- * %APPLOADER_NO_ERROR on success.
- */
-struct apploader_load_app_req {
- uint64_t package_size;
-} __packed;
-
-/**
- * apploader_resp - Common header for all apploader responses
- * @hdr - header with command value.
- * @error - error code returned by peer; one of &enum apploader_error values.
- *
- * This structure is followed by the response-specific payload, if the command
- * has one.
- */
-struct apploader_resp {
- struct apploader_header hdr;
- uint32_t error;
-} __packed;
diff --git a/trusty/apploader/fuzz/Android.bp b/trusty/apploader/fuzz/Android.bp
deleted file mode 100644
index e37dab1b0..000000000
--- a/trusty/apploader/fuzz/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-// Fuzz Trusty IPC messages sent to apploader.
-cc_fuzz {
- name: "trusty_apploader_tipc_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: [":trusty_tipc_fuzzer"],
- cflags: [
- "-DTRUSTY_APP_PORT=\"com.android.trusty.apploader\"",
- "-DTRUSTY_APP_UUID=\"081ba88f-f1ee-452e-b5e8-a7e9ef173a97\"",
- "-DTRUSTY_APP_FILENAME=\"apploader.syms.elf\"",
- ]
-}
-
-// Fuzz app package sent to apploader.
-cc_fuzz {
- name: "trusty_apploader_app_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: ["app_fuzzer.cpp"],
- include_dirs: ["system/core/trusty/apploader"],
- shared_libs: [
- "libdmabufheap",
- ],
-}
diff --git a/trusty/apploader/fuzz/app_fuzzer.cpp b/trusty/apploader/fuzz/app_fuzzer.cpp
deleted file mode 100644
index aa0caca81..000000000
--- a/trusty/apploader/fuzz/app_fuzzer.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <BufferAllocator/BufferAllocator.h>
-#include <android-base/unique_fd.h>
-#include <apploader_ipc.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <trusty/tipc.h>
-#include <unistd.h>
-#include <iostream>
-
-using android::base::unique_fd;
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define APPLOADER_PORT "com.android.trusty.apploader"
-#define APPLOADER_MODULE_NAME "apploader.syms.elf"
-
-/* Apploader TA's UUID is 081ba88f-f1ee-452e-b5e8-a7e9ef173a97 */
-static struct uuid apploader_uuid = {
- 0x081ba88f,
- 0xf1ee,
- 0x452e,
- {0xb5, 0xe8, 0xa7, 0xe9, 0xef, 0x17, 0x3a, 0x97},
-};
-
-static inline uintptr_t RoundPageUp(uintptr_t val) {
- return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
-static bool SendLoadMsg(int chan, int dma_buf, size_t dma_buf_size) {
- apploader_header hdr = {
- .cmd = APPLOADER_CMD_LOAD_APPLICATION,
- };
- apploader_load_app_req req = {
- .package_size = static_cast<uint64_t>(dma_buf_size),
- };
- iovec iov[] = {
- {
- .iov_base = &hdr,
- .iov_len = sizeof(hdr),
- },
- {
- .iov_base = &req,
- .iov_len = sizeof(req),
- },
- };
- trusty_shm shm = {
- .fd = dma_buf,
- .transfer = TRUSTY_SHARE,
- };
-
- int rc = tipc_send(chan, iov, 2, &shm, 1);
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(req))) {
- std::cerr << "Failed to send request" << std::endl;
- return false;
- }
-
- apploader_resp resp;
- rc = read(chan, &resp, sizeof(resp));
- if (rc != static_cast<int>(sizeof(resp))) {
- std::cerr << "Failed to receive response" << std::endl;
- return false;
- }
-
- return true;
-}
-
-static CoverageRecord record(TIPC_DEV, &apploader_uuid, APPLOADER_MODULE_NAME);
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
- auto ret = record.Open();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- exit(-1);
- }
- return 0;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- ExtraCounters counters(&record);
- counters.Reset();
-
- android::trusty::fuzz::TrustyApp ta(TIPC_DEV, APPLOADER_PORT);
- auto ret = ta.Connect();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- android::trusty::fuzz::Abort();
- }
-
- uint64_t shm_len = size ? RoundPageUp(size) : PAGE_SIZE;
- BufferAllocator alloc;
- unique_fd dma_buf(alloc.Alloc(kDmabufSystemHeapName, shm_len));
- if (dma_buf < 0) {
- std::cerr << "Failed to create dmabuf of size: " << shm_len << std::endl;
- android::trusty::fuzz::Abort();
- }
-
- void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
- if (shm_base == MAP_FAILED) {
- std::cerr << "Failed to mmap() dmabuf" << std::endl;
- android::trusty::fuzz::Abort();
- }
-
- memcpy(shm_base, data, size);
-
- bool success = SendLoadMsg(*ta.GetRawFd(), dma_buf, shm_len);
- if (!success) {
- std::cerr << "Failed to send load message" << std::endl;
- android::trusty::fuzz::Abort();
- }
-
- munmap(shm_base, shm_len);
- return 0;
-}
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
index 092241528..60e0e71b2 100644
--- a/trusty/confirmationui/Android.bp
+++ b/trusty/confirmationui/Android.bp
@@ -19,10 +19,6 @@
// to only building on ARM if they include assembly. Individual makefiles
// are responsible for having their own logic, for fine-grained control.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "android.hardware.confirmationui@1.0-service.trusty",
relative_install_path: "hw",
@@ -58,7 +54,6 @@ cc_library {
"android.hardware.confirmationui@1.0",
"android.hardware.keymaster@4.0",
"libbase",
- "libdmabufheap",
"libhidlbase",
"libteeui_hal_support",
"libtrusty",
@@ -97,4 +92,4 @@ cc_library {
"-Werror",
"-DTEEUI_USE_STD_VECTOR",
],
-}
+} \ No newline at end of file
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
index 18e45cd35..3d9a2d6e5 100644
--- a/trusty/confirmationui/NotSoSecureInput.cpp
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -82,7 +82,7 @@ Nonce generateNonce() {
/**
* This is an implementation of the SecureInput protocol in unserspace. This is
- * just an example and should not be used as is. The protocol implemented here
+ * just an example and should not be used as is. The protocol implemented her
* should be used by a trusted input device that can assert user events with
* high assurance even if the HLOS kernel is compromised. A confirmationui HAL
* that links directly against this implementation is not secure and shal not be
diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp
index cee8655e0..e4c68f955 100644
--- a/trusty/confirmationui/TrustyApp.cpp
+++ b/trusty/confirmationui/TrustyApp.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, The Android Open Source Project
+ * Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,152 +16,141 @@
#include "TrustyApp.h"
-#include <BufferAllocator/BufferAllocator.h>
#include <android-base/logging.h>
-#include <sys/mman.h>
#include <sys/uio.h>
#include <trusty/tipc.h>
-#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
-
namespace android {
namespace trusty {
-namespace confirmationui {
-
-using ::android::base::unique_fd;
-
-static inline uintptr_t RoundPageUp(uintptr_t val) {
- return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
-ssize_t TrustyApp::TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
- uint8_t* iend) {
- uint32_t olen = oend - obegin;
-
- if (olen > shm_len_) {
- LOG(ERROR) << AT << "request message too long to fit in shared memory";
- return -1;
- }
- memcpy(shm_base_, obegin, olen);
-
- confirmationui_hdr hdr = {
- .cmd = CONFIRMATIONUI_CMD_MSG,
- };
- confirmationui_msg_args args = {
- .msg_len = olen,
- };
- iovec iov[] = {
- {
- .iov_base = &hdr,
- .iov_len = sizeof(hdr),
- },
- {
- .iov_base = &args,
- .iov_len = sizeof(args),
- },
- };
-
- int rc = tipc_send(handle_, iov, countof(iov), NULL, 0);
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
- LOG(ERROR) << AT << "failed to send MSG request";
- return -1;
- }
-
- rc = readv(handle_, iov, countof(iov));
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
- LOG(ERROR) << AT << "failed to receive MSG response";
- return -1;
- }
-
- if (hdr.cmd != (CONFIRMATIONUI_CMD_MSG | CONFIRMATIONUI_RESP_BIT)) {
- LOG(ERROR) << AT << "unknown response command: " << hdr.cmd;
- return -1;
+// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
+// This assures that packets can always be read/written in one read/write operation.
+static constexpr const uint32_t kPacketSize = 0x1000 - 32;
+
+enum class PacketType : uint32_t {
+ SND,
+ RCV,
+ ACK,
+};
+
+struct PacketHeader {
+ PacketType type;
+ uint32_t remaining;
+};
+
+const char* toString(PacketType t) {
+ switch (t) {
+ case PacketType::SND:
+ return "SND";
+ case PacketType::RCV:
+ return "RCV";
+ case PacketType::ACK:
+ return "ACK";
+ default:
+ return "UNKNOWN";
}
+}
- uint32_t ilen = iend - ibegin;
- if (args.msg_len > ilen) {
- LOG(ERROR) << AT << "response message too long to fit in return buffer";
- return -1;
+static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
+static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+ uint8_t* iend) {
+ while (obegin != oend) {
+ PacketHeader header = {
+ .type = PacketType::SND,
+ .remaining = uint32_t(oend - obegin),
+ };
+ uint32_t body_size = std::min(kPayloadSize, header.remaining);
+ iovec iov[] = {
+ {
+ .iov_base = &header,
+ .iov_len = kHeaderSize,
+ },
+ {
+ .iov_base = const_cast<uint8_t*>(obegin),
+ .iov_len = body_size,
+ },
+ };
+ int rc = writev(handle, iov, 2);
+ if (!rc) {
+ PLOG(ERROR) << "Error sending SND message. " << rc;
+ return rc;
+ }
+
+ obegin += body_size;
+
+ rc = read(handle, &header, kHeaderSize);
+ if (!rc) {
+ PLOG(ERROR) << "Error reading ACK. " << rc;
+ return rc;
+ }
+
+ if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
+ LOG(ERROR) << "malformed ACK";
+ return -1;
+ }
}
- memcpy(ibegin, shm_base_, args.msg_len);
-
- return args.msg_len;
+ ssize_t remaining = 0;
+ auto begin = ibegin;
+ do {
+ PacketHeader header = {
+ .type = PacketType::RCV,
+ .remaining = 0,
+ };
+
+ iovec iov[] = {
+ {
+ .iov_base = &header,
+ .iov_len = kHeaderSize,
+ },
+ {
+ .iov_base = begin,
+ .iov_len = uint32_t(iend - begin),
+ },
+ };
+
+ ssize_t rc = writev(handle, iov, 1);
+ if (!rc) {
+ PLOG(ERROR) << "Error sending RCV message. " << rc;
+ return rc;
+ }
+
+ rc = readv(handle, iov, 2);
+ if (rc < 0) {
+ PLOG(ERROR) << "Error reading response. " << rc;
+ return rc;
+ }
+
+ uint32_t body_size = std::min(kPayloadSize, header.remaining);
+ if (body_size != rc - kHeaderSize) {
+ LOG(ERROR) << "Unexpected amount of data: " << rc;
+ return -1;
+ }
+
+ remaining = header.remaining - body_size;
+ begin += body_size;
+ } while (remaining);
+
+ return begin - ibegin;
}
TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
: handle_(kInvalidHandle) {
- unique_fd tipc_handle(tipc_connect(path.c_str(), appname.c_str()));
- if (tipc_handle < 0) {
+ handle_ = tipc_connect(path.c_str(), appname.c_str());
+ if (handle_ == kInvalidHandle) {
LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
<< "\"" << path << "\"";
- return;
}
-
- uint32_t shm_len = RoundPageUp(CONFIRMATIONUI_MAX_MSG_SIZE);
- BufferAllocator allocator;
- unique_fd dma_buf(allocator.Alloc("system", shm_len));
- if (dma_buf < 0) {
- LOG(ERROR) << AT << "failed to allocate shared memory buffer";
- return;
- }
-
- confirmationui_hdr hdr = {
- .cmd = CONFIRMATIONUI_CMD_INIT,
- };
- confirmationui_init_req args = {
- .shm_len = shm_len,
- };
- iovec iov[] = {
- {
- .iov_base = &hdr,
- .iov_len = sizeof(hdr),
- },
- {
- .iov_base = &args,
- .iov_len = sizeof(args),
- },
- };
- trusty_shm shm = {
- .fd = dma_buf,
- .transfer = TRUSTY_SHARE,
- };
-
- int rc = tipc_send(tipc_handle, iov, 2, &shm, 1);
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
- LOG(ERROR) << AT << "failed to send INIT request";
- return;
- }
-
- rc = read(tipc_handle, &hdr, sizeof(hdr));
- if (rc != static_cast<int>(sizeof(hdr))) {
- LOG(ERROR) << AT << "failed to receive INIT response";
- return;
- }
-
- if (hdr.cmd != (CONFIRMATIONUI_CMD_INIT | CONFIRMATIONUI_RESP_BIT)) {
- LOG(ERROR) << AT << "unknown response command: " << hdr.cmd;
- return;
- }
-
- void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
- if (shm_base == MAP_FAILED) {
- LOG(ERROR) << AT << "failed to mmap() shared memory buffer";
- return;
- }
-
- handle_ = std::move(tipc_handle);
- shm_base_ = shm_base;
- shm_len_ = shm_len;
-
LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
}
-
TrustyApp::~TrustyApp() {
+ if (handle_ != kInvalidHandle) {
+ tipc_close(handle_);
+ }
LOG(INFO) << "Done shutting down TrustyApp";
}
-} // namespace confirmationui
} // namespace trusty
} // namespace android
diff --git a/trusty/confirmationui/TrustyApp.h b/trusty/confirmationui/TrustyApp.h
index fb57828f9..05a25f61d 100644
--- a/trusty/confirmationui/TrustyApp.h
+++ b/trusty/confirmationui/TrustyApp.h
@@ -16,10 +16,7 @@
#pragma once
-#include <TrustyIpc.h>
-
#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
@@ -41,7 +38,6 @@
namespace android {
namespace trusty {
-namespace confirmationui {
using ::teeui::Message;
using ::teeui::msg2tuple_t;
@@ -64,11 +60,19 @@ enum class TrustyAppError : int32_t {
MSG_TOO_LONG = -2,
};
+/*
+ * There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol
+ * overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign
+ * mode of operation.
+ */
+static constexpr const size_t kSendBufferSize = 0x2000;
+
+ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+ uint8_t* iend);
+
class TrustyApp {
private:
- android::base::unique_fd handle_;
- void* shm_base_;
- size_t shm_len_;
+ int handle_;
static constexpr const int kInvalidHandle = -1;
/*
* This mutex serializes communication with the trusted app, not handle_.
@@ -80,8 +84,6 @@ class TrustyApp {
TrustyApp(const std::string& path, const std::string& appname);
~TrustyApp();
- ssize_t TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin, uint8_t* iend);
-
template <typename Request, typename Response, typename... T>
std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
std::lock_guard<std::mutex> lock(mutex_);
@@ -91,7 +93,7 @@ class TrustyApp {
return {TrustyAppError::ERROR, {}};
}
- uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];
+ uint8_t buffer[kSendBufferSize];
WriteStream out(buffer);
out = write(Request(), out, args...);
@@ -100,8 +102,8 @@ class TrustyApp {
return {TrustyAppError::MSG_TOO_LONG, {}};
}
- auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
- &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);
+ auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+ &buffer[kSendBufferSize]);
if (rc < 0) return {TrustyAppError::ERROR, {}};
ReadStream in(&buffer[0], rc);
@@ -123,7 +125,7 @@ class TrustyApp {
return TrustyAppError::ERROR;
}
- uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];
+ uint8_t buffer[kSendBufferSize];
WriteStream out(buffer);
out = write(Request(), out, args...);
@@ -132,8 +134,8 @@ class TrustyApp {
return TrustyAppError::MSG_TOO_LONG;
}
- auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
- &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);
+ auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+ &buffer[kSendBufferSize]);
if (rc < 0) {
LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
return TrustyAppError::ERROR;
@@ -149,6 +151,5 @@ class TrustyApp {
operator bool() const { return handle_ != kInvalidHandle; }
};
-} // namespace confirmationui
} // namespace trusty
} // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
index c6625e0a1..6b25893b5 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.cpp
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -50,7 +50,7 @@ namespace implementation {
using namespace secure_input;
-using ::android::trusty::confirmationui::TrustyAppError;
+using ::android::trusty::TrustyAppError;
using ::teeui::AbortMsg;
using ::teeui::DeliverTestCommandMessage;
@@ -71,7 +71,7 @@ using ::std::tie;
using TeeuiRc = ::teeui::ResponseCode;
constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
-constexpr const char kConfirmationuiAppName[] = CONFIRMATIONUI_PORT;
+constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui";
namespace {
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
index 0bd703c9f..3a7c7ef4a 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.h
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -43,7 +43,7 @@ using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
-using ::android::trusty::confirmationui::TrustyApp;
+using ::android::trusty::TrustyApp;
class TrustyConfirmationUI : public IConfirmationUI {
public:
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
index 3ba6fc04b..dc7a03b6e 100644
--- a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
@@ -1,4 +1,4 @@
service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
class hal
- user system
- group drmrpc input system
+ user nobody
+ group drmrpc input
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
deleted file mode 100644
index ba571914d..000000000
--- a/trusty/confirmationui/fuzz/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_fuzz {
- name: "trusty_confirmationui_tipc_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: [":trusty_tipc_fuzzer"],
- cflags: [
- "-DTRUSTY_APP_PORT=\"com.android.trusty.confirmationui\"",
- "-DTRUSTY_APP_UUID=\"7dee2364-c036-425b-b086-df0f6c233c1b\"",
- "-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
- ],
-
-}
-
-cc_fuzz {
- name: "trusty_confirmationui_msg_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: ["msg_fuzzer.cpp"],
- include_dirs: ["system/core/trusty/confirmationui/include"],
- shared_libs: [
- "libdmabufheap",
- ],
-
- // The initial corpus for this fuzzer was derived by dumping messages from/to
- // HAL to/from TA triggered by VtsHalConfirmationUIV1_0TargetTest.
- corpus: ["msg_corpus/*"],
-}
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIl b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIl
deleted file mode 100644
index c8741fbdf..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIl
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOdd b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOdd
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOdd
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumR b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumR
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumR
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64b b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64b
deleted file mode 100644
index 168242791..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64b
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCK b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCK
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCK
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPi b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPi
deleted file mode 100644
index d48e5a167..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPi
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Ko b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Ko
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Ko
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABY b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABY
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABY
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUq b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUq
deleted file mode 100644
index 6f72ad5c5..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUq
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vff b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vff
deleted file mode 100644
index 64a159c58..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vff
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTST b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTST
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTST
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055ei b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055ei
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055ei
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEm b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEm
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEm
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbb
deleted file mode 100644
index 64a159c58..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbb
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7t b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7t
deleted file mode 100644
index 64a159c58..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7t
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzu b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzu
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzu
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5
deleted file mode 100644
index 556828d24..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rP b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rP
deleted file mode 100644
index 8be96c587..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rP
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj
deleted file mode 100644
index e69de29bb..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj
+++ /dev/null
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHa b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHa
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHa
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWz b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWz
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWz
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4U b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4U
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4U
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hT b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hT
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hT
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZ
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZ
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vG b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vG
deleted file mode 100644
index b944d949a..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vG
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpX b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpX
deleted file mode 100644
index 1d9374d4b..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpX
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWz b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWz
deleted file mode 100644
index b3be8cd7a..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWz
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKC
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKC
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFn
deleted file mode 100644
index 4190adf33..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFn
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSS b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSS
deleted file mode 100644
index 1d9374d4b..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSS
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30
deleted file mode 100644
index 38e3fca1c..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnC
deleted file mode 100644
index 4190adf33..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnC
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNn
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNn
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZi b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZi
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZi
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofv b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofv
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofv
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAG b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAG
deleted file mode 100644
index 4190adf33..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAG
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbn
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbn
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzs b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzs
deleted file mode 100644
index cbfd07a97..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzs
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBC
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBC
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZp b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZp
deleted file mode 100644
index ecaec1233..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZp
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfE b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfE
deleted file mode 100644
index 58b1526a2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfE
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1r b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1r
deleted file mode 100644
index af570ea46..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1r
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LL b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LL
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LL
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXs b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXs
deleted file mode 100644
index e4b99fb7a..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXs
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5ang b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5ang
deleted file mode 100644
index d1149560d..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5ang
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedb
deleted file mode 100644
index 6caf7ddb6..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedb
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUt b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUt
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUt
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14
deleted file mode 100644
index ecaec1233..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTw b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTw
deleted file mode 100644
index 36445d957..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTw
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJC
deleted file mode 100644
index f6c6dcf6b..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJC
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDH b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDH
deleted file mode 100644
index d6ba1fc10..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDH
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRC
deleted file mode 100644
index 7392034c2..000000000
--- a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRC
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_fuzzer.cpp b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
deleted file mode 100644
index 8e4443c78..000000000
--- a/trusty/confirmationui/fuzz/msg_fuzzer.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <BufferAllocator/BufferAllocator.h>
-#include <TrustyIpc.h>
-#include <iostream>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <time.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <trusty/tipc.h>
-#include <unistd.h>
-
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
-#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
-
-/* A request to render to screen may take a while. */
-const size_t kTimeoutSeconds = 30;
-
-/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
-static struct uuid confirmationui_uuid = {
- 0x7dee2364,
- 0xc036,
- 0x425b,
- {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b},
-};
-
-static CoverageRecord record(TIPC_DEV, &confirmationui_uuid, CONFIRMATIONUI_MODULE_NAME);
-
-static android::base::unique_fd dma_buf;
-static void* shm_base;
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
- auto ret = record.Open();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- exit(-1);
- }
-
- BufferAllocator allocator;
- dma_buf.reset(allocator.Alloc(kDmabufSystemHeapName, CONFIRMATIONUI_MAX_MSG_SIZE));
- if (dma_buf < 0) {
- std::cerr << "Failed to allocate dma_buf" << std::endl;
- exit(-1);
- }
-
- shm_base = mmap(0, CONFIRMATIONUI_MAX_MSG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
- if (shm_base == MAP_FAILED) {
- std::cerr << "Failed to mmap() dma_buf" << std::endl;
- exit(-1);
- }
-
- return 0;
-}
-
-static bool Init(int chan, int dma_buf) {
- confirmationui_hdr hdr = {
- .cmd = CONFIRMATIONUI_CMD_INIT,
- };
- confirmationui_init_req args = {
- .shm_len = CONFIRMATIONUI_MAX_MSG_SIZE,
- };
- iovec iov[] = {
- {
- .iov_base = &hdr,
- .iov_len = sizeof(hdr),
- },
- {
- .iov_base = &args,
- .iov_len = sizeof(args),
- },
- };
- trusty_shm shm = {
- .fd = dma_buf,
- .transfer = TRUSTY_SHARE,
- };
-
- int rc = tipc_send(chan, iov, countof(iov), &shm, 1);
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
- return false;
- }
-
- rc = read(chan, &hdr, sizeof(hdr));
- if (rc != static_cast<int>(sizeof(hdr))) {
- return false;
- }
-
- return true;
-}
-
-static bool Msg(int chan, const uint8_t* data, size_t size) {
- confirmationui_hdr hdr = {
- .cmd = CONFIRMATIONUI_CMD_MSG,
- };
- confirmationui_msg_args args = {
- .msg_len = static_cast<uint32_t>(size),
- };
- iovec iov[] = {
- {
- .iov_base = &hdr,
- .iov_len = sizeof(hdr),
- },
- {
- .iov_base = &args,
- .iov_len = sizeof(args),
- },
- };
-
- memset(shm_base, 0, CONFIRMATIONUI_MAX_MSG_SIZE);
- memcpy(shm_base, data, size);
-
- int rc = tipc_send(chan, iov, countof(iov), NULL, 0);
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
- return false;
- }
-
- rc = readv(chan, iov, countof(iov));
- if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
- return false;
- }
-
- return true;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- ExtraCounters counters(&record);
- counters.Reset();
-
- TrustyApp ta(TIPC_DEV, CONFIRMATIONUI_PORT);
- auto ret = ta.Connect();
- if (!ret.ok()) {
- android::trusty::fuzz::Abort();
- }
- int chan = *ta.GetRawFd();
-
- alarm(kTimeoutSeconds);
- bool success = Init(chan, dma_buf);
- alarm(0);
- if (!success) {
- android::trusty::fuzz::Abort();
- }
-
- alarm(kTimeoutSeconds);
- success = Msg(chan, data, size);
- alarm(0);
- if (!success) {
- android::trusty::fuzz::Abort();
- }
-
- return 0;
-}
diff --git a/trusty/confirmationui/include/TrustyIpc.h b/trusty/confirmationui/include/TrustyIpc.h
deleted file mode 100644
index eb764bc46..000000000
--- a/trusty/confirmationui/include/TrustyIpc.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-/*
- * This interface is shared between Android and Trusty. There is a copy in each
- * repository. They must be kept in sync.
- */
-
-#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
-
-/**
- * enum confirmationui_cmd - command identifiers for ConfirmationUI interface
- * @CONFIRMATIONUI_RESP_BIT: response bit set as part of response
- * @CONFIRMATIONUI_REQ_SHIFT: number of bits used by response bit
- * @CONFIRMATIONUI_CMD_INIT: command to initialize session
- * @CONFIRMATIONUI_CMD_MSG: command to send ConfirmationUI messages
- */
-enum confirmationui_cmd : uint32_t {
- CONFIRMATIONUI_RESP_BIT = 1,
- CONFIRMATIONUI_REQ_SHIFT = 1,
-
- CONFIRMATIONUI_CMD_INIT = (1 << CONFIRMATIONUI_REQ_SHIFT),
- CONFIRMATIONUI_CMD_MSG = (2 << CONFIRMATIONUI_REQ_SHIFT),
-};
-
-/**
- * struct confirmationui_hdr - header for ConfirmationUI messages
- * @cmd: command identifier
- *
- * Note that no messages return a status code. Any error on the server side
- * results in the connection being closed. So, operations can be assumed to be
- * successful if they return a response.
- */
-struct confirmationui_hdr {
- uint32_t cmd;
-};
-
-/**
- * struct confirmationui_init_req - arguments for request to initialize a
- * session
- * @shm_len: length of memory region being shared
- *
- * A handle to a memory region must be sent along with this message. This memory
- * is send to ConfirmationUI messages.
- */
-struct confirmationui_init_req {
- uint32_t shm_len;
-};
-
-/**
- * struct confirmationui_msg_args - arguments for sending a message
- * @msg_len: length of message being sent
- *
- * Contents of the message are located in the shared memory region that is
- * established using %CONFIRMATIONUI_CMD_INIT.
- *
- * ConfirmationUI messages can travel both ways.
- */
-struct confirmationui_msg_args {
- uint32_t msg_len;
-};
-
-#define CONFIRMATIONUI_MAX_MSG_SIZE 0x2000
diff --git a/trusty/coverage/Android.bp b/trusty/coverage/Android.bp
deleted file mode 100644
index 0453f3f51..000000000
--- a/trusty/coverage/Android.bp
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2020 The Android Open-Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library {
- name: "libtrusty_coverage",
- vendor_available: true,
- srcs: [
- "coverage.cpp",
- "uuid.cpp",
- ],
- export_include_dirs: [
- "include",
- ],
- shared_libs: [
- "libbase",
- "libext2_uuid",
- "liblog",
- "libdmabufheap",
- "libtrusty",
- ],
-}
-
-cc_test {
- name: "libtrusty_coverage_test",
- srcs: [
- "coverage_test.cpp",
- ],
- static_libs: [
- "libtrusty_coverage",
- "libtrusty",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- "libdmabufheap",
- ],
- require_root: true,
-}
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp
deleted file mode 100644
index 3c6b5c510..000000000
--- a/trusty/coverage/coverage.cpp
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Sourete Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "coverage"
-
-#include <BufferAllocator/BufferAllocator.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <assert.h>
-#include <log/log.h>
-#include <stdio.h>
-#include <sys/mman.h>
-#include <sys/uio.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/coverage/record.h>
-#include <trusty/coverage/tipc.h>
-#include <trusty/tipc.h>
-#include <iostream>
-
-#define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client"
-
-namespace android {
-namespace trusty {
-namespace coverage {
-
-using android::base::ErrnoError;
-using android::base::Error;
-using std::string;
-using std::to_string;
-using std::unique_ptr;
-
-static inline uintptr_t RoundPageUp(uintptr_t val) {
- return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
-CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
- : tipc_dev_(std::move(tipc_dev)),
- coverage_srv_fd_(-1),
- uuid_(*uuid),
- sancov_filename_(),
- record_len_(0),
- shm_(NULL),
- shm_len_(0) {}
-
-CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid, string module_name)
- : tipc_dev_(std::move(tipc_dev)),
- coverage_srv_fd_(-1),
- uuid_(*uuid),
- sancov_filename_(module_name + "." + to_string(getpid()) + ".sancov"),
- record_len_(0),
- shm_(NULL),
- shm_len_(0) {}
-
-CoverageRecord::~CoverageRecord() {
- if (shm_) {
- if (sancov_filename_) {
- auto res = SaveSancovFile(*sancov_filename_);
- if (!res.ok()) {
- ALOGE("Could not write sancov file for module: %s\n", sancov_filename_->c_str());
- }
- }
-
- munmap((void*)shm_, shm_len_);
- }
-}
-
-Result<void> CoverageRecord::Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp) {
- int rc;
-
- if (req_fd < 0) {
- rc = write(coverage_srv_fd_, req, sizeof(*req));
- } else {
- iovec iov = {
- .iov_base = req,
- .iov_len = sizeof(*req),
- };
-
- trusty_shm shm = {
- .fd = req_fd,
- .transfer = TRUSTY_SHARE,
- };
-
- rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);
- }
-
- if (rc != (int)sizeof(*req)) {
- return ErrnoError() << "failed to send request to coverage server: ";
- }
-
- rc = read(coverage_srv_fd_, resp, sizeof(*resp));
- if (rc != (int)sizeof(*resp)) {
- return ErrnoError() << "failed to read reply from coverage server: ";
- }
-
- if (resp->hdr.cmd != (req->hdr.cmd | COVERAGE_CLIENT_CMD_RESP_BIT)) {
- return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd;
- }
-
- return {};
-}
-
-Result<void> CoverageRecord::Open() {
- coverage_client_req req;
- coverage_client_resp resp;
-
- if (shm_) {
- return {}; /* already initialized */
- }
-
- int fd = tipc_connect(tipc_dev_.c_str(), COVERAGE_CLIENT_PORT);
- if (fd < 0) {
- // Don't error out to support fuzzing builds without coverage, e.g. for repros.
- std::cerr << "WARNING!!! Failed to connect to Trusty coverarge server." << std::endl;
- return {};
- }
- coverage_srv_fd_.reset(fd);
-
- req.hdr.cmd = COVERAGE_CLIENT_CMD_OPEN;
- req.open_args.uuid = uuid_;
- auto ret = Rpc(&req, -1, &resp);
- if (!ret.ok()) {
- return Error() << "failed to open coverage client: " << ret.error();
- }
- record_len_ = resp.open_args.record_len;
- shm_len_ = RoundPageUp(record_len_);
-
- BufferAllocator allocator;
-
- fd = allocator.Alloc("system", shm_len_);
- if (fd < 0) {
- return ErrnoError() << "failed to create dmabuf of size " << shm_len_
- << " err code: " << fd;
- }
- unique_fd dma_buf(fd);
-
- void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
- if (shm == MAP_FAILED) {
- return ErrnoError() << "failed to map memfd: ";
- }
-
- req.hdr.cmd = COVERAGE_CLIENT_CMD_SHARE_RECORD;
- req.share_record_args.shm_len = shm_len_;
- ret = Rpc(&req, dma_buf, &resp);
- if (!ret.ok()) {
- return Error() << "failed to send shared memory: " << ret.error();
- }
-
- shm_ = shm;
- return {};
-}
-
-bool CoverageRecord::IsOpen() {
- return shm_;
-}
-
-void CoverageRecord::ResetFullRecord() {
- auto header_region = GetRegionBounds(COV_START);
- if (!header_region.ok()) {
- // If the header cannot be parsed, we can't reset the proper region yet.
- return;
- }
-
- for (size_t i = header_region->second; i < shm_len_; i++) {
- *((volatile uint8_t*)shm_ + i) = 0;
- }
-}
-
-void CoverageRecord::ResetCounts() {
- volatile uint8_t* begin = nullptr;
- volatile uint8_t* end = nullptr;
- GetRawCounts(&begin, &end);
-
- for (volatile uint8_t* x = begin; x < end; x++) {
- *x = 0;
- }
-}
-
-void CoverageRecord::ResetPCs() {
- volatile uintptr_t* begin = nullptr;
- volatile uintptr_t* end = nullptr;
- GetRawPCs(&begin, &end);
-
- for (volatile uintptr_t* x = begin; x < end; x++) {
- *x = 0;
- }
-}
-
-Result<std::pair<size_t, size_t>> CoverageRecord::GetRegionBounds(uint32_t region_type) {
- assert(shm_);
-
- auto header = (volatile struct coverage_record_header*)shm_;
-
- if (header->type != COV_START) {
- return Error() << "Header not yet valid";
- }
-
- for (++header; header->type != COV_TOTAL_LENGTH; ++header) {
- if (header->type == region_type) {
- // Coverage record must end with a COV_TOTAL_LENGTH header entry, so
- // it is always safe to read the next entry since we don't iterate
- // over the COV_TOTAL_LENGTH entry.
- return {{header->offset, (header + 1)->offset}};
- }
- }
-
- return Error() << "Could not find coverage region type: " << region_type;
-}
-
-void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) {
- assert(shm_);
-
- *begin = shm_;
- *end = (uint8_t*)(*begin) + record_len_;
-}
-
-void CoverageRecord::GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end) {
- auto region = GetRegionBounds(COV_8BIT_COUNTERS);
- if (!region.ok()) {
- *begin = 0;
- *end = 0;
- return;
- }
-
- assert(region->second <= record_len_);
-
- *begin = (volatile uint8_t*)shm_ + region->first;
- *end = (volatile uint8_t*)shm_ + region->second;
-}
-
-void CoverageRecord::GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end) {
- auto region = GetRegionBounds(COV_INSTR_PCS);
- if (!region.ok()) {
- *begin = 0;
- *end = 0;
- return;
- }
-
- assert(region->second <= record_len_);
-
- *begin = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->first);
- *end = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->second);
-}
-
-uint64_t CoverageRecord::TotalEdgeCounts() {
- assert(shm_);
-
- uint64_t counter = 0;
-
- volatile uint8_t* begin = NULL;
- volatile uint8_t* end = NULL;
-
- GetRawCounts(&begin, &end);
-
- for (volatile uint8_t* x = begin; x < end; x++) {
- counter += *x;
- }
-
- return counter;
-}
-
-Result<void> CoverageRecord::SaveSancovFile(const std::string& filename) {
- android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
- if (!output_fd.ok()) {
- return ErrnoError() << "Could not open sancov file";
- }
-
- uint64_t magic;
- if (sizeof(uintptr_t) == 8) {
- magic = 0xC0BFFFFFFFFFFF64;
- } else if (sizeof(uintptr_t) == 4) {
- magic = 0xC0BFFFFFFFFFFF32;
- }
- WriteFully(output_fd, &magic, sizeof(magic));
-
- volatile uintptr_t* begin = nullptr;
- volatile uintptr_t* end = nullptr;
-
- GetRawPCs(&begin, &end);
-
- for (volatile uintptr_t* pc_ptr = begin; pc_ptr < end; pc_ptr++) {
- uintptr_t pc = *pc_ptr;
- if (pc) {
- WriteFully(output_fd, &pc, sizeof(pc));
- }
- }
-
- return {};
-}
-
-} // namespace coverage
-} // namespace trusty
-} // namespace android
diff --git a/trusty/coverage/coverage_test.cpp b/trusty/coverage/coverage_test.cpp
deleted file mode 100644
index c1efca63b..000000000
--- a/trusty/coverage/coverage_test.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/stringprintf.h>
-#include <gtest/gtest.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/tipc.h>
-#include <array>
-#include <memory>
-
-using android::base::unique_fd;
-using std::array;
-using std::make_unique;
-using std::unique_ptr;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
-#define TEST_SRV_MODULE "srv.syms.elf"
-
-namespace android {
-namespace trusty {
-namespace coverage {
-
-/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */
-static struct uuid test_srv_uuid = {
- 0x77f68803,
- 0xc514,
- 0x43ba,
- {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24},
-};
-
-class CoverageTest : public ::testing::Test {
- public:
- void SetUp() override {
- record_ = make_unique<CoverageRecord>(TIPC_DEV, &test_srv_uuid);
- auto ret = record_->Open();
- ASSERT_TRUE(ret.ok()) << ret.error();
- }
-
- void TearDown() override { record_.reset(); }
-
- unique_ptr<CoverageRecord> record_;
-};
-
-TEST_F(CoverageTest, CoverageReset) {
- record_->ResetFullRecord();
- auto counter = record_->TotalEdgeCounts();
- ASSERT_EQ(counter, 0);
-}
-
-TEST_F(CoverageTest, TestServerCoverage) {
- unique_fd test_srv(tipc_connect(TIPC_DEV, TEST_SRV_PORT));
- ASSERT_GE(test_srv, 0);
-
- uint32_t mask = (uint32_t)-1;
- uint32_t magic = 0xdeadbeef;
- uint64_t high_watermark = 0;
-
- for (size_t i = 1; i < sizeof(magic) * 8; i++) {
- /* Reset coverage */
- record_->ResetCounts();
-
- /* Send message to test server */
- uint32_t msg = magic & ~(mask << i);
- int rc = write(test_srv, &msg, sizeof(msg));
- ASSERT_EQ(rc, sizeof(msg));
-
- /* Read message from test server */
- rc = read(test_srv, &msg, sizeof(msg));
- ASSERT_EQ(rc, sizeof(msg));
-
- /* Count number of non-unique blocks executed */
- auto counter = record_->TotalEdgeCounts();
- /* Each consecutive input should exercise more or same blocks */
- ASSERT_GE(counter, high_watermark);
- high_watermark = counter;
-
- auto sancov_filename = android::base::StringPrintf(
- "/data/local/tmp/" TEST_SRV_MODULE ".%d.sancov", getpid());
- auto res = record_->SaveSancovFile(sancov_filename);
- ASSERT_TRUE(res.ok());
- }
-
- ASSERT_GT(high_watermark, 0);
-}
-
-} // namespace coverage
-} // namespace trusty
-} // namespace android
diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h
deleted file mode 100644
index 9ccc9813a..000000000
--- a/trusty/coverage/include/trusty/coverage/coverage.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <optional>
-#include <string>
-
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-#include <stdint.h>
-#include <trusty/coverage/tipc.h>
-
-namespace android {
-namespace trusty {
-namespace coverage {
-
-using android::base::Result;
-using android::base::unique_fd;
-
-class CoverageRecord {
- public:
- /**
- * Create a coverage record interface. Coverage will not be written to a
- * sancov output file on completion.
- */
- CoverageRecord(std::string tipc_dev, struct uuid* uuid);
-
- /**
- * Create a coverage record interface. On destruction, write this coverage
- * to the given sancov filename.
- */
- CoverageRecord(std::string tipc_dev, struct uuid* uuid, std::string module_name);
-
- ~CoverageRecord();
- Result<void> Open();
- bool IsOpen();
- void ResetFullRecord();
- void ResetCounts();
- void ResetPCs();
- void GetRawData(volatile void** begin, volatile void** end);
- void GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end);
- void GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end);
- uint64_t TotalEdgeCounts();
-
- /**
- * Save the current set of observed PCs to the given filename.
- * The resulting .sancov file can be parsed via the LLVM sancov tool to see
- * coverage statistics and visualize coverage.
- */
- Result<void> SaveSancovFile(const std::string& filename);
-
- private:
- Result<void> Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp);
-
- Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
-
- std::string tipc_dev_;
- unique_fd coverage_srv_fd_;
- struct uuid uuid_;
- std::optional<std::string> sancov_filename_;
- size_t record_len_;
- volatile void* shm_;
- size_t shm_len_;
-};
-
-} // namespace coverage
-} // namespace trusty
-} // namespace android
diff --git a/trusty/coverage/include/trusty/coverage/record.h b/trusty/coverage/include/trusty/coverage/record.h
deleted file mode 100644
index bfe06f335..000000000
--- a/trusty/coverage/include/trusty/coverage/record.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* This file needs to be kept in-sync with its counterpart on Trusty side:
- * trusty/user/base/lib/coverage/common/include/lib/coverage/common/record.h */
-
-#pragma once
-
-#include <stdint.h>
-
-/**
- * enum coverage_record_type - Coverage region header type
- * @COV_START: Magic header start marker
- * @COV_8BIT_COUNTERS: 8bit counter for each instrumentation point
- * @COV_INSTR_PCS: Pointer length offset of each instrumentation point from the
- * start of the binary
- * @COV_TOTAL_LENGTH: Total length of the entire coverage record, must be the
- * last header item.
- *
- * Describes the type of a region of the coverage record. See &struct
- * coverage_record_header.
- */
-enum coverage_record_type {
- COV_START = 0x434f5652,
- COV_8BIT_COUNTERS = 1,
- COV_INSTR_PCS = 2,
- COV_TOTAL_LENGTH = 0,
-};
-
-/**
- * struct coverage_record_header - Header entry describing a region of the
- * coverage record.
- * @type: type of the region, must be one of @enum coverage_record_type
- * @offset: offset from the beginning of the header to the start of the region
- *
- * Coverage records start with a header which is a list of struct
- * coverage_record_header, beginning with an entry with type COV_START and
- * terminated with an entry with type COV_TOTAL_LENGTH. Each of these header
- * entries corresponds to a region of the record, with the offset indicating the
- * offset of the start of that region from the beginning of the record (i.e. the
- * beginning of the header). Each record type and offset is 32-bit field with
- * native endianness. The first header item must be COV_START with a 0 offset.
- * The COV_START entry should be initialized when the coverage header is
- * complete and ready for consumption by the client, because coverage record
- * initialization happens asynchronously. The final header item,
- * COV_TOTAL_LENGTH, which must always be present, indicates the total length of
- * the coverage record, including the header.
- *
- * Coverage regions should be contiguous, so the end of one region is the start
- * of the next, and the coverage header must be in the same order as the regions
- * in the record body. Thus we can compute the length of a region by subtracting
- * the region's offset from the offset of the next header item.
- */
-struct coverage_record_header {
- uint32_t type;
- uint32_t offset;
-};
diff --git a/trusty/coverage/include/trusty/coverage/tipc.h b/trusty/coverage/include/trusty/coverage/tipc.h
deleted file mode 100644
index 386b2bb63..000000000
--- a/trusty/coverage/include/trusty/coverage/tipc.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* This file needs to be kept in-sync with it's counterpart on Trusty side */
-
-#pragma once
-
-#include <stdint.h>
-#include <trusty/coverage/uuid.h>
-
-#define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client"
-
-enum coverage_client_cmd {
- COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
- COVERAGE_CLIENT_CMD_SHIFT = 1U,
- COVERAGE_CLIENT_CMD_OPEN = (1U << COVERAGE_CLIENT_CMD_SHIFT),
- COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << COVERAGE_CLIENT_CMD_SHIFT),
-};
-
-struct coverage_client_hdr {
- uint32_t cmd;
-};
-
-struct coverage_client_open_req {
- struct uuid uuid;
-};
-
-struct coverage_client_open_resp {
- uint32_t record_len;
-};
-
-struct coverage_client_share_record_req {
- uint32_t shm_len;
-};
-
-struct coverage_client_req {
- struct coverage_client_hdr hdr;
- union {
- struct coverage_client_open_req open_args;
- struct coverage_client_share_record_req share_record_args;
- };
-};
-
-struct coverage_client_resp {
- struct coverage_client_hdr hdr;
- union {
- struct coverage_client_open_resp open_args;
- };
-};
diff --git a/trusty/coverage/include/trusty/coverage/uuid.h b/trusty/coverage/include/trusty/coverage/uuid.h
deleted file mode 100644
index c77d27508..000000000
--- a/trusty/coverage/include/trusty/coverage/uuid.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-struct uuid {
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_hi_and_version;
- uint8_t clock_seq_and_node[8];
-};
-
-/**
- * str_to_uuid() - Converts a C string into a uuid
- * @str: C-string representation of the uuid
- * @uuid: &struct uuid to fill with the converted uuid
- *
- * Return: true on success, false otherwise
- */
-bool str_to_uuid(const char* str, struct uuid* uuid);
diff --git a/trusty/coverage/uuid.cpp b/trusty/coverage/uuid.cpp
deleted file mode 100644
index f0a6c0ea7..000000000
--- a/trusty/coverage/uuid.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Sourete Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string.h>
-#include <trusty/coverage/uuid.h>
-#include <uuid.h>
-
-#include <stdio.h>
-
-static uint16_t reverse_u16(uint16_t u) {
- return u << 8 | u >> 8;
-}
-
-static uint32_t reverse_u32(uint32_t u) {
- return reverse_u16((uint16_t)u) << 16 | reverse_u16(u >> 16);
-}
-
-bool str_to_uuid(const char* str, struct uuid* uuid) {
- uuid_t uu;
- static_assert(sizeof(uu) == sizeof(*uuid));
-
- if (uuid_parse(str, uu)) {
- return false;
- }
-
- memcpy(uuid, uu, sizeof(*uuid));
- uuid->time_low = reverse_u32(uuid->time_low);
- uuid->time_mid = reverse_u16(uuid->time_mid);
- uuid->time_hi_and_version = reverse_u16(uuid->time_hi_and_version);
- return true;
-}
diff --git a/trusty/fuzz/Android.bp b/trusty/fuzz/Android.bp
deleted file mode 100644
index 5d0ff7920..000000000
--- a/trusty/fuzz/Android.bp
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_defaults {
- name: "trusty_fuzzer_defaults",
- shared_libs: [
- "libtrusty",
- "libtrusty_coverage",
- "libtrusty_fuzz_utils",
- "libbase",
- "liblog",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- fuzz_config: {
- fuzz_on_haiku_host: false,
- },
-}
-
-cc_library {
- name: "libtrusty_fuzz_utils",
- srcs: [
- "counters.cpp",
- "utils.cpp",
- ],
- export_include_dirs: ["include"],
- static_libs: [
- "libFuzzer",
- ],
- shared_libs: [
- "libtrusty_coverage",
- "libbase",
- "liblog",
- "libtrusty",
- ],
-}
-
-// Generic TIPC fuzzer, must parameterized using:
-// -DTRUSTY_APP_PORT=<port name of TA being fuzzed>
-// -DTRUSTY_APP_UUID=<UUID of TA being fuzzed>
-// -DTRUSTY_APP_FILENAME=<name of symbolized elf binary of the TA>
-filegroup {
- name: "trusty_tipc_fuzzer",
- srcs: ["tipc_fuzzer.cpp"],
-}
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
deleted file mode 100644
index c28fd05a5..000000000
--- a/trusty/fuzz/counters.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Sourete Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "trusty-fuzz-counters"
-
-#include <FuzzerDefs.h>
-
-#include <trusty/fuzz/counters.h>
-
-#include <android-base/logging.h>
-#include <log/log.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/coverage/tipc.h>
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-
-/*
- * We don't know how many counters the coverage record will contain. So, eyeball
- * the size of this section.
- */
-static const size_t kMaxNumCounters = 0x8000;
-__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
-
-namespace android {
-namespace trusty {
-namespace fuzz {
-
-ExtraCounters::ExtraCounters(coverage::CoverageRecord* record) : record_(record) {
- if (!record_->IsOpen()) {
- return;
- }
-
- assert(fuzzer::ExtraCountersBegin());
- assert(fuzzer::ExtraCountersEnd());
-
- volatile uint8_t* begin = NULL;
- volatile uint8_t* end = NULL;
- record_->GetRawCounts(&begin, &end);
- assert(end - begin <= sizeof(counters));
-}
-
-ExtraCounters::~ExtraCounters() {
- if (!record_->IsOpen()) {
- return;
- }
-
- Flush();
-}
-
-void ExtraCounters::Reset() {
- if (!record_->IsOpen()) {
- return;
- }
-
- record_->ResetCounts();
- fuzzer::ClearExtraCounters();
-}
-
-void ExtraCounters::Flush() {
- volatile uint8_t* begin = NULL;
- volatile uint8_t* end = NULL;
-
- record_->GetRawCounts(&begin, &end);
- if (!begin || !end) {
- ALOGE("Could not get raw counts from coverage record\n");
- return;
- }
-
- size_t num_counters = end - begin;
- if (num_counters > kMaxNumCounters) {
- ALOGE("Too many counters (%zu) to fit in the extra counters section!\n", num_counters);
- num_counters = kMaxNumCounters;
- }
- for (size_t i = 0; i < num_counters; i++) {
- *(counters + i) = *(begin + i);
- }
-}
-
-} // namespace fuzz
-} // namespace trusty
-} // namespace android
diff --git a/trusty/fuzz/include/trusty/fuzz/counters.h b/trusty/fuzz/include/trusty/fuzz/counters.h
deleted file mode 100644
index db933d9b6..000000000
--- a/trusty/fuzz/include/trusty/fuzz/counters.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include <android-base/result.h>
-#include <trusty/coverage/coverage.h>
-
-namespace android {
-namespace trusty {
-namespace fuzz {
-
-class ExtraCounters {
- public:
- ExtraCounters(coverage::CoverageRecord* record);
- ~ExtraCounters();
-
- void Reset();
- void Flush();
-
- private:
- coverage::CoverageRecord* record_;
-};
-
-} // namespace fuzz
-} // namespace trusty
-} // namespace android
diff --git a/trusty/fuzz/include/trusty/fuzz/utils.h b/trusty/fuzz/include/trusty/fuzz/utils.h
deleted file mode 100644
index c90641258..000000000
--- a/trusty/fuzz/include/trusty/fuzz/utils.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-
-#define TIPC_MAX_MSG_SIZE PAGE_SIZE
-
-namespace android {
-namespace trusty {
-namespace fuzz {
-
-class TrustyApp {
- public:
- TrustyApp(std::string tipc_dev, std::string ta_port);
-
- android::base::Result<void> Connect();
- android::base::Result<void> Read(void* buf, size_t len);
- android::base::Result<void> Write(const void* buf, size_t len);
- void Disconnect();
-
- android::base::Result<int> GetRawFd();
-
- private:
- std::string tipc_dev_;
- std::string ta_port_;
- android::base::unique_fd ta_fd_;
-};
-
-void Abort();
-
-} // namespace fuzz
-} // namespace trusty
-} // namespace android
diff --git a/trusty/fuzz/test/Android.bp b/trusty/fuzz/test/Android.bp
deleted file mode 100644
index e0bca55b1..000000000
--- a/trusty/fuzz/test/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_fuzz {
- name: "trusty_test_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: [":trusty_tipc_fuzzer"],
- cflags: [
- "-DTRUSTY_APP_PORT=\"com.android.trusty.sancov.test.srv\"",
- "-DTRUSTY_APP_UUID=\"77f68803-c514-43ba-bdce-3254531c3d24\"",
- "-DTRUSTY_APP_FILENAME=\"srv.syms.elf\"",
- ],
- fuzz_config: {
- fuzz_on_haiku_device: false,
- },
-}
diff --git a/trusty/fuzz/tipc_fuzzer.cpp b/trusty/fuzz/tipc_fuzzer.cpp
deleted file mode 100644
index f265cedb6..000000000
--- a/trusty/fuzz/tipc_fuzzer.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/coverage/uuid.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <unistd.h>
-#include <iostream>
-#include <memory>
-
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-
-#ifndef TRUSTY_APP_PORT
-#error "Port name must be parameterized using -DTRUSTY_APP_PORT."
-#endif
-
-#ifndef TRUSTY_APP_UUID
-#error "UUID must be parameterized using -DTRUSTY_APP_UUID."
-#endif
-
-#ifndef TRUSTY_APP_FILENAME
-#error "Binary file name must be parameterized using -DTRUSTY_APP_FILENAME."
-#endif
-
-static TrustyApp kTrustyApp(TIPC_DEV, TRUSTY_APP_PORT);
-static std::unique_ptr<CoverageRecord> record;
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
- uuid module_uuid;
-
- if (!str_to_uuid(TRUSTY_APP_UUID, &module_uuid)) {
- std::cerr << "Failed to parse UUID: " << TRUSTY_APP_UUID << std::endl;
- exit(-1);
- }
-
- /* Make sure lazy-loaded TAs have started and connected to coverage service. */
- auto ret = kTrustyApp.Connect();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- exit(-1);
- }
-
- record = std::make_unique<CoverageRecord>(TIPC_DEV, &module_uuid, TRUSTY_APP_FILENAME);
- if (!record) {
- std::cerr << "Failed to allocate coverage record" << std::endl;
- exit(-1);
- }
-
- ret = record->Open();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- exit(-1);
- }
- return 0;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- static uint8_t buf[TIPC_MAX_MSG_SIZE];
-
- ExtraCounters counters(record.get());
- counters.Reset();
-
- auto ret = kTrustyApp.Write(data, size);
- if (ret.ok()) {
- ret = kTrustyApp.Read(&buf, sizeof(buf));
- }
-
- // Reconnect to ensure that the service is still up
- kTrustyApp.Disconnect();
- ret = kTrustyApp.Connect();
- if (!ret.ok()) {
- std::cerr << ret.error() << std::endl;
- android::trusty::fuzz::Abort();
- }
-
- return ret.ok() ? 0 : -1;
-}
diff --git a/trusty/fuzz/utils.cpp b/trusty/fuzz/utils.cpp
deleted file mode 100644
index bb096beab..000000000
--- a/trusty/fuzz/utils.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Sourete Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "trusty-fuzz-utils"
-
-#include <trusty/fuzz/utils.h>
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#include <linux/uio.h>
-#include <log/log_read.h>
-#include <time.h>
-#include <trusty/tipc.h>
-#include <iostream>
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::base::unique_fd;
-
-namespace {
-
-const size_t kTimeoutSeconds = 5;
-const std::string kTrustyLogTag = "trusty-log";
-
-const time_t kInitialTime = time(nullptr);
-
-void PrintTrustyLog() {
- auto logger_list = android_logger_list_open(LOG_ID_KERNEL, ANDROID_LOG_NONBLOCK, 1000, 0);
- if (logger_list == nullptr) {
- std::cerr << "Could not open android kernel log\n";
- return;
- }
-
- while (true) {
- log_msg log_msg;
- int rc = android_logger_list_read(logger_list, &log_msg);
- if (rc < 0) {
- break;
- }
- if (log_msg.entry.sec < kInitialTime) {
- continue;
- }
- char* msg = log_msg.msg();
- if (msg) {
- std::string line(msg, log_msg.entry.len);
- if (line.find(kTrustyLogTag) != std::string::npos) {
- std::cerr << line.substr(kTrustyLogTag.length() + 2) << std::endl;
- }
- }
- }
-
- android_logger_list_free(logger_list);
-}
-
-} // namespace
-
-namespace android {
-namespace trusty {
-namespace fuzz {
-
-TrustyApp::TrustyApp(std::string tipc_dev, std::string ta_port)
- : tipc_dev_(tipc_dev), ta_port_(ta_port), ta_fd_(-1) {}
-
-Result<void> TrustyApp::Connect() {
- alarm(kTimeoutSeconds);
- int fd = tipc_connect(tipc_dev_.c_str(), ta_port_.c_str());
- alarm(0);
- if (fd < 0) {
- return ErrnoError() << "failed to open TIPC device: ";
- }
- ta_fd_.reset(fd);
-
- return {};
-}
-
-Result<void> TrustyApp::Read(void* buf, size_t len) {
- if (ta_fd_ == -1) {
- return Error() << "TA is not connected to yet: ";
- }
-
- alarm(kTimeoutSeconds);
- int rc = read(ta_fd_, buf, len);
- alarm(0);
- if (rc < 0) {
- return Error() << "failed to read TIPC message from TA: ";
- }
-
- return {};
-}
-
-Result<void> TrustyApp::Write(const void* buf, size_t len) {
- if (ta_fd_ == -1) {
- return Error() << "TA is not connected to yet: ";
- }
-
- alarm(kTimeoutSeconds);
- int rc = write(ta_fd_, buf, len);
- alarm(0);
- if (rc < 0) {
- return Error() << "failed to write TIPC message to TA: ";
- }
-
- return {};
-}
-
-Result<int> TrustyApp::GetRawFd() {
- if (ta_fd_ == -1) {
- return Error() << "TA is not connected to yet: ";
- }
-
- return ta_fd_;
-}
-
-void TrustyApp::Disconnect() {
- ta_fd_.reset();
-}
-
-void Abort() {
- PrintTrustyLog();
- exit(-1);
-}
-
-} // namespace fuzz
-} // namespace trusty
-} // namespace android
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 81f012fdc..e553af1b5 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -19,10 +19,6 @@
// to only building on ARM if they include assembly. Individual makefiles
// are responsible for having their own logic, for fine-grained control.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "android.hardware.gatekeeper@1.0-service.trusty",
defaults: ["hidl_defaults"],
diff --git a/trusty/gatekeeper/fuzz/Android.bp b/trusty/gatekeeper/fuzz/Android.bp
deleted file mode 100644
index d084cb6b2..000000000
--- a/trusty/gatekeeper/fuzz/Android.bp
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_fuzz {
- name: "trusty_gatekeeper_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: [":trusty_tipc_fuzzer"],
- cflags: [
- "-DTRUSTY_APP_PORT=\"com.android.trusty.gatekeeper\"",
- "-DTRUSTY_APP_UUID=\"38ba0cdc-df0e-11e4-9869-233fb6ae4795\"",
- "-DTRUSTY_APP_FILENAME=\"gatekeeper.syms.elf\"",
- ],
-
- // The initial corpus for this fuzzer was derived by dumping messages from
- // the `secure_env` emulator interface for cuttlefish while enrolling a new
- // password in the emulator.
- corpus: ["corpus/*"],
-}
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-2MMzSr b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-2MMzSr
deleted file mode 100644
index f3c1f79b7..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-2MMzSr
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Et63W0 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Et63W0
deleted file mode 100644
index b3e65859f..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Et63W0
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-G41Iz8 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-G41Iz8
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-G41Iz8
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-ItEoqJ b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-ItEoqJ
deleted file mode 100644
index 85d38c710..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-ItEoqJ
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-MGXdfu b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-MGXdfu
deleted file mode 100644
index f8e146720..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-MGXdfu
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Yq4f10 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Yq4f10
deleted file mode 100644
index c221077c1..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-Yq4f10
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-agxKZa b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-agxKZa
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-agxKZa
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-alhn2v b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-alhn2v
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-alhn2v
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-eVJFHV b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-eVJFHV
deleted file mode 100644
index f3c1f79b7..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-eVJFHV
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-et5K21 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-et5K21
deleted file mode 100644
index f3c1f79b7..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-et5K21
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-gun5YX b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-gun5YX
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-gun5YX
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-kXw1R9 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-kXw1R9
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-kXw1R9
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-moapss b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-moapss
deleted file mode 100644
index 85d38c710..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-moapss
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-u5QySb b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-u5QySb
deleted file mode 100644
index 09f9d746e..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-u5QySb
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-uZtvkq b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-uZtvkq
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-uZtvkq
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-w5G2SF b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-w5G2SF
deleted file mode 100644
index d42956deb..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-w5G2SF
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-y3H74x b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-y3H74x
deleted file mode 100644
index 1cec41389..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-y3H74x
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-yALfeS b/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-yALfeS
deleted file mode 100644
index f3c1f79b7..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-recv-yALfeS
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-2S1GLi b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-2S1GLi
deleted file mode 100644
index 08b344914..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-2S1GLi
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-4j7hUc b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-4j7hUc
deleted file mode 100644
index 550740052..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-4j7hUc
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-6hsSQG b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-6hsSQG
deleted file mode 100644
index ffa74cb7c..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-6hsSQG
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-E8CE7b b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-E8CE7b
deleted file mode 100644
index 21cdd9c7a..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-E8CE7b
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-GEDmHj b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-GEDmHj
deleted file mode 100644
index 23a8c0810..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-GEDmHj
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-MpwDEN b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-MpwDEN
deleted file mode 100644
index 1795d0982..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-MpwDEN
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Qutf8O b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Qutf8O
deleted file mode 100644
index 4f69edfd4..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Qutf8O
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Sg1WMt b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Sg1WMt
deleted file mode 100644
index ba6d1cbce..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Sg1WMt
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-U6Y1My b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-U6Y1My
deleted file mode 100644
index 631ef79f2..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-U6Y1My
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-WdSRky b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-WdSRky
deleted file mode 100644
index 02d4820eb..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-WdSRky
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Ypw6WP b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Ypw6WP
deleted file mode 100644
index 6d7574fd5..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Ypw6WP
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Yyj4Af b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Yyj4Af
deleted file mode 100644
index 47f518dc8..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-Yyj4Af
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-amyF62 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-amyF62
deleted file mode 100644
index 3a5fdf5e8..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-amyF62
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-gu8ziA b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-gu8ziA
deleted file mode 100644
index bab5da1e6..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-gu8ziA
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-iCATsM b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-iCATsM
deleted file mode 100644
index fae917357..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-iCATsM
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-kawT3I b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-kawT3I
deleted file mode 100644
index 51e363021..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-kawT3I
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-sYFzM5 b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-sYFzM5
deleted file mode 100644
index 173d77eaf..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-sYFzM5
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-yNFMdn b/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-yNFMdn
deleted file mode 100644
index 96f9e4240..000000000
--- a/trusty/gatekeeper/fuzz/corpus/gatekeeper-send-yNFMdn
+++ /dev/null
Binary files differ
diff --git a/trusty/gatekeeper/gatekeeper_ipc.h b/trusty/gatekeeper/gatekeeper_ipc.h
index 8709d1a08..b05dcd848 100644
--- a/trusty/gatekeeper/gatekeeper_ipc.h
+++ b/trusty/gatekeeper/gatekeeper_ipc.h
@@ -20,13 +20,11 @@
#define GATEKEEPER_MAX_BUFFER_LENGTH 1024
enum gatekeeper_command {
- GK_REQ_SHIFT = 1,
- GK_RESP_BIT = 1,
+ GK_REQ_SHIFT = 1,
+ GK_RESP_BIT = 1,
- GK_ENROLL = (0 << GK_REQ_SHIFT),
- GK_VERIFY = (1 << GK_REQ_SHIFT),
- GK_DELETE_USER = (2 << GK_REQ_SHIFT),
- GK_DELETE_ALL_USERS = (3 << GK_REQ_SHIFT),
+ GK_ENROLL = (0 << GK_REQ_SHIFT),
+ GK_VERIFY = (1 << GK_REQ_SHIFT),
};
/**
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index ec4f81bf7..d14966460 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -56,9 +56,9 @@ TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
- auto buffer = new uint8_t[vec.size()];
- std::copy(vec.begin(), vec.end(), buffer);
- return {buffer, static_cast<uint32_t>(vec.size())};
+ auto dummy = new uint8_t[vec.size()];
+ std::copy(vec.begin(), vec.end(), dummy);
+ return {dummy, static_cast<uint32_t>(vec.size())};
}
Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
@@ -133,48 +133,13 @@ Return<void> TrustyGateKeeperDevice::verify(
return {};
}
-Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) {
- if (error_ != 0) {
- _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
- return {};
- }
-
- DeleteUserRequest request(uid);
- DeleteUserResponse response;
- auto error = Send(request, &response);
-
- if (error != ERROR_NONE) {
- _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
- } else if (response.error == ERROR_NOT_IMPLEMENTED) {
- _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
- } else if (response.error != ERROR_NONE) {
- _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
- } else {
- _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, {}});
- }
+Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t /*uid*/, deleteUser_cb _hidl_cb) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
return {};
}
Return<void> TrustyGateKeeperDevice::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
- if (error_ != 0) {
- _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
- return {};
- }
-
- DeleteAllUsersRequest request;
- DeleteAllUsersResponse response;
- auto error = Send(request, &response);
-
- if (error != ERROR_NONE) {
- _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
- } else if (response.error == ERROR_NOT_IMPLEMENTED) {
- _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
- } else if (response.error != ERROR_NONE) {
- _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
- } else {
- _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, {}});
- }
-
+ _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
return {};
}
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
index 420dd7a09..c0713f4b9 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.h
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -81,15 +81,6 @@ class TrustyGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGa
return Send(GK_VERIFY, request, response);
}
- gatekeeper_error_t Send(const DeleteUserRequest& request, DeleteUserResponse* response) {
- return Send(GK_DELETE_USER, request, response);
- }
-
- gatekeeper_error_t Send(const DeleteAllUsersRequest& request,
- DeleteAllUsersResponse* response) {
- return Send(GK_DELETE_ALL_USERS, request, response);
- }
-
int error_;
};
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index d787f7a97..98cbcc3f8 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -221,10 +221,10 @@ Return<void> TrustyKeymaster3Device::getHardwareFeatures(getHardwareFeatures_cb
Return<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
if (data.size() == 0) return ErrorCode::OK;
- AddEntropyRequest request(impl_->message_version());
+ AddEntropyRequest request;
request.random_data.Reinitialize(data.data(), data.size());
- AddEntropyResponse response(impl_->message_version());
+ AddEntropyResponse response;
impl_->AddRngEntropy(request, &response);
return legacy_enum_conversion(response.error);
@@ -232,10 +232,10 @@ Return<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>&
Return<void> TrustyKeymaster3Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
generateKey_cb _hidl_cb) {
- GenerateKeyRequest request(impl_->message_version());
+ GenerateKeyRequest request;
request.key_description.Reinitialize(KmParamSet(keyParams));
- GenerateKeyResponse response(impl_->message_version());
+ GenerateKeyResponse response;
impl_->GenerateKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -253,11 +253,11 @@ Return<void> TrustyKeymaster3Device::getKeyCharacteristics(const hidl_vec<uint8_
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
getKeyCharacteristics_cb _hidl_cb) {
- GetKeyCharacteristicsRequest request(impl_->message_version());
+ GetKeyCharacteristicsRequest request;
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- GetKeyCharacteristicsResponse response(impl_->message_version());
+ GetKeyCharacteristicsResponse response;
impl_->GetKeyCharacteristics(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -273,12 +273,12 @@ Return<void> TrustyKeymaster3Device::importKey(const hidl_vec<KeyParameter>& par
KeyFormat keyFormat,
const hidl_vec<uint8_t>& keyData,
importKey_cb _hidl_cb) {
- ImportKeyRequest request(impl_->message_version());
+ ImportKeyRequest request;
request.key_description.Reinitialize(KmParamSet(params));
request.key_format = legacy_enum_conversion(keyFormat);
- request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
+ request.SetKeyMaterial(keyData.data(), keyData.size());
- ImportKeyResponse response(impl_->message_version());
+ ImportKeyResponse response;
impl_->ImportKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -297,12 +297,12 @@ Return<void> TrustyKeymaster3Device::exportKey(KeyFormat exportFormat,
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
exportKey_cb _hidl_cb) {
- ExportKeyRequest request(impl_->message_version());
+ ExportKeyRequest request;
request.key_format = legacy_enum_conversion(exportFormat);
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- ExportKeyResponse response(impl_->message_version());
+ ExportKeyResponse response;
impl_->ExportKey(request, &response);
hidl_vec<uint8_t> resultKeyBlob;
@@ -316,11 +316,11 @@ Return<void> TrustyKeymaster3Device::exportKey(KeyFormat exportFormat,
Return<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
const hidl_vec<KeyParameter>& attestParams,
attestKey_cb _hidl_cb) {
- AttestKeyRequest request(impl_->message_version());
+ AttestKeyRequest request;
request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
request.attest_params.Reinitialize(KmParamSet(attestParams));
- AttestKeyResponse response(impl_->message_version());
+ AttestKeyResponse response;
impl_->AttestKey(request, &response);
hidl_vec<hidl_vec<uint8_t>> resultCertChain;
@@ -334,11 +334,11 @@ Return<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAtt
Return<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
const hidl_vec<KeyParameter>& upgradeParams,
upgradeKey_cb _hidl_cb) {
- UpgradeKeyRequest request(impl_->message_version());
+ UpgradeKeyRequest request;
request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
- UpgradeKeyResponse response(impl_->message_version());
+ UpgradeKeyResponse response;
impl_->UpgradeKey(request, &response);
if (response.error == KM_ERROR_OK) {
@@ -350,18 +350,18 @@ Return<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlob
}
Return<ErrorCode> TrustyKeymaster3Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
- DeleteKeyRequest request(impl_->message_version());
+ DeleteKeyRequest request;
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- DeleteKeyResponse response(impl_->message_version());
+ DeleteKeyResponse response;
impl_->DeleteKey(request, &response);
return legacy_enum_conversion(response.error);
}
Return<ErrorCode> TrustyKeymaster3Device::deleteAllKeys() {
- DeleteAllKeysRequest request(impl_->message_version());
- DeleteAllKeysResponse response(impl_->message_version());
+ DeleteAllKeysRequest request;
+ DeleteAllKeysResponse response;
impl_->DeleteAllKeys(request, &response);
return legacy_enum_conversion(response.error);
@@ -374,15 +374,15 @@ Return<ErrorCode> TrustyKeymaster3Device::destroyAttestationIds() {
Return<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
const hidl_vec<KeyParameter>& inParams,
begin_cb _hidl_cb) {
- BeginOperationRequest request(impl_->message_version());
+ BeginOperationRequest request;
request.purpose = legacy_enum_conversion(purpose);
request.SetKeyMaterial(key.data(), key.size());
request.additional_params.Reinitialize(KmParamSet(inParams));
- BeginOperationResponse response(impl_->message_version());
+ BeginOperationResponse response;
impl_->BeginOperation(request, &response);
- hidl_vec<KeyParameter> resultParams(impl_->message_version());
+ hidl_vec<KeyParameter> resultParams;
if (response.error == KM_ERROR_OK) {
resultParams = kmParamSet2Hidl(response.output_params);
}
@@ -394,8 +394,8 @@ Return<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<ui
Return<void> TrustyKeymaster3Device::update(uint64_t operationHandle,
const hidl_vec<KeyParameter>& inParams,
const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
- UpdateOperationRequest request(impl_->message_version());
- UpdateOperationResponse response(impl_->message_version());
+ UpdateOperationRequest request;
+ UpdateOperationResponse response;
hidl_vec<KeyParameter> resultParams;
hidl_vec<uint8_t> resultBlob;
uint32_t resultConsumed = 0;
@@ -431,13 +431,13 @@ Return<void> TrustyKeymaster3Device::finish(uint64_t operationHandle,
const hidl_vec<uint8_t>& input,
const hidl_vec<uint8_t>& signature,
finish_cb _hidl_cb) {
- FinishOperationRequest request(impl_->message_version());
+ FinishOperationRequest request;
request.op_handle = operationHandle;
request.input.Reinitialize(input.data(), input.size());
request.signature.Reinitialize(signature.data(), signature.size());
request.additional_params.Reinitialize(KmParamSet(inParams));
- FinishOperationResponse response(impl_->message_version());
+ FinishOperationResponse response;
impl_->FinishOperation(request, &response);
hidl_vec<KeyParameter> resultParams;
@@ -451,10 +451,10 @@ Return<void> TrustyKeymaster3Device::finish(uint64_t operationHandle,
}
Return<ErrorCode> TrustyKeymaster3Device::abort(uint64_t operationHandle) {
- AbortOperationRequest request(impl_->message_version());
+ AbortOperationRequest request;
request.op_handle = operationHandle;
- AbortOperationResponse response(impl_->message_version());
+ AbortOperationResponse response;
impl_->AbortOperation(request, &response);
return legacy_enum_conversion(response.error);
diff --git a/trusty/keymaster/3.0/service.cpp b/trusty/keymaster/3.0/service.cpp
index b916c3760..0d8436eff 100644
--- a/trusty/keymaster/3.0/service.cpp
+++ b/trusty/keymaster/3.0/service.cpp
@@ -24,7 +24,7 @@
int main() {
::android::hardware::configureRpcThreadpool(1, true);
auto trustyKeymaster = new keymaster::TrustyKeymaster();
- int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_3);
+ int err = trustyKeymaster->Initialize();
if (err != 0) {
LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
return -1;
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index e68ba82dc..ec2ba12ed 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -284,7 +284,7 @@ Return<void> TrustyKeymaster4Device::getHmacSharingParameters(
Return<void> TrustyKeymaster4Device::computeSharedHmac(
const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
- ComputeSharedHmacRequest request(impl_->message_version());
+ ComputeSharedHmacRequest request;
request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
request.params_array.num_params = params.size();
for (size_t i = 0; i < params.size(); ++i) {
@@ -309,7 +309,7 @@ Return<void> TrustyKeymaster4Device::computeSharedHmac(
Return<void> TrustyKeymaster4Device::verifyAuthorization(
uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
- VerifyAuthorizationRequest request(impl_->message_version());
+ VerifyAuthorizationRequest request;
request.challenge = challenge;
request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
request.auth_token.challenge = authToken.challenge;
@@ -336,10 +336,10 @@ Return<void> TrustyKeymaster4Device::verifyAuthorization(
Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
if (data.size() == 0) return ErrorCode::OK;
- AddEntropyRequest request(impl_->message_version());
+ AddEntropyRequest request;
request.random_data.Reinitialize(data.data(), data.size());
- AddEntropyResponse response(impl_->message_version());
+ AddEntropyResponse response;
impl_->AddRngEntropy(request, &response);
return legacy_enum_conversion(response.error);
@@ -347,10 +347,10 @@ Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>&
Return<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
generateKey_cb _hidl_cb) {
- GenerateKeyRequest request(impl_->message_version());
+ GenerateKeyRequest request;
request.key_description.Reinitialize(KmParamSet(keyParams));
- GenerateKeyResponse response(impl_->message_version());
+ GenerateKeyResponse response;
impl_->GenerateKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -368,11 +368,11 @@ Return<void> TrustyKeymaster4Device::getKeyCharacteristics(const hidl_vec<uint8_
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
getKeyCharacteristics_cb _hidl_cb) {
- GetKeyCharacteristicsRequest request(impl_->message_version());
+ GetKeyCharacteristicsRequest request;
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- GetKeyCharacteristicsResponse response(impl_->message_version());
+ GetKeyCharacteristicsResponse response;
impl_->GetKeyCharacteristics(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -388,12 +388,12 @@ Return<void> TrustyKeymaster4Device::importKey(const hidl_vec<KeyParameter>& par
KeyFormat keyFormat,
const hidl_vec<uint8_t>& keyData,
importKey_cb _hidl_cb) {
- ImportKeyRequest request(impl_->message_version());
+ ImportKeyRequest request;
request.key_description.Reinitialize(KmParamSet(params));
request.key_format = legacy_enum_conversion(keyFormat);
- request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
+ request.SetKeyMaterial(keyData.data(), keyData.size());
- ImportKeyResponse response(impl_->message_version());
+ ImportKeyResponse response;
impl_->ImportKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -411,7 +411,7 @@ Return<void> TrustyKeymaster4Device::importWrappedKey(
const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
- ImportWrappedKeyRequest request(impl_->message_version());
+ ImportWrappedKeyRequest request;
request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
@@ -419,7 +419,7 @@ Return<void> TrustyKeymaster4Device::importWrappedKey(
request.password_sid = passwordSid;
request.biometric_sid = biometricSid;
- ImportWrappedKeyResponse response(impl_->message_version());
+ ImportWrappedKeyResponse response;
impl_->ImportWrappedKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -438,12 +438,12 @@ Return<void> TrustyKeymaster4Device::exportKey(KeyFormat exportFormat,
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
exportKey_cb _hidl_cb) {
- ExportKeyRequest request(impl_->message_version());
+ ExportKeyRequest request;
request.key_format = legacy_enum_conversion(exportFormat);
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- ExportKeyResponse response(impl_->message_version());
+ ExportKeyResponse response;
impl_->ExportKey(request, &response);
hidl_vec<uint8_t> resultKeyBlob;
@@ -457,11 +457,11 @@ Return<void> TrustyKeymaster4Device::exportKey(KeyFormat exportFormat,
Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
const hidl_vec<KeyParameter>& attestParams,
attestKey_cb _hidl_cb) {
- AttestKeyRequest request(impl_->message_version());
+ AttestKeyRequest request;
request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
request.attest_params.Reinitialize(KmParamSet(attestParams));
- AttestKeyResponse response(impl_->message_version());
+ AttestKeyResponse response;
impl_->AttestKey(request, &response);
hidl_vec<hidl_vec<uint8_t>> resultCertChain;
@@ -475,11 +475,11 @@ Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAtt
Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
const hidl_vec<KeyParameter>& upgradeParams,
upgradeKey_cb _hidl_cb) {
- UpgradeKeyRequest request(impl_->message_version());
+ UpgradeKeyRequest request;
request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
- UpgradeKeyResponse response(impl_->message_version());
+ UpgradeKeyResponse response;
impl_->UpgradeKey(request, &response);
if (response.error == KM_ERROR_OK) {
@@ -491,18 +491,18 @@ Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlob
}
Return<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
- DeleteKeyRequest request(impl_->message_version());
+ DeleteKeyRequest request;
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- DeleteKeyResponse response(impl_->message_version());
+ DeleteKeyResponse response;
impl_->DeleteKey(request, &response);
return legacy_enum_conversion(response.error);
}
Return<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {
- DeleteAllKeysRequest request(impl_->message_version());
- DeleteAllKeysResponse response(impl_->message_version());
+ DeleteAllKeysRequest request;
+ DeleteAllKeysResponse response;
impl_->DeleteAllKeys(request, &response);
return legacy_enum_conversion(response.error);
@@ -516,12 +516,12 @@ Return<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<ui
const hidl_vec<KeyParameter>& inParams,
const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
- BeginOperationRequest request(impl_->message_version());
+ BeginOperationRequest request;
request.purpose = legacy_enum_conversion(purpose);
request.SetKeyMaterial(key.data(), key.size());
request.additional_params.Reinitialize(KmParamSet(extendedParams));
- BeginOperationResponse response(impl_->message_version());
+ BeginOperationResponse response;
impl_->BeginOperation(request, &response);
hidl_vec<KeyParameter> resultParams;
@@ -540,8 +540,8 @@ Return<void> TrustyKeymaster4Device::update(uint64_t operationHandle,
const VerificationToken& verificationToken,
update_cb _hidl_cb) {
(void)verificationToken;
- UpdateOperationRequest request(impl_->message_version());
- UpdateOperationResponse response(impl_->message_version());
+ UpdateOperationRequest request;
+ UpdateOperationResponse response;
hidl_vec<KeyParameter> resultParams;
hidl_vec<uint8_t> resultBlob;
hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
@@ -581,14 +581,14 @@ Return<void> TrustyKeymaster4Device::finish(uint64_t operationHandle,
const VerificationToken& verificationToken,
finish_cb _hidl_cb) {
(void)verificationToken;
- FinishOperationRequest request(impl_->message_version());
+ FinishOperationRequest request;
hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
request.op_handle = operationHandle;
request.input.Reinitialize(input.data(), input.size());
request.signature.Reinitialize(signature.data(), signature.size());
request.additional_params.Reinitialize(KmParamSet(extendedParams));
- FinishOperationResponse response(impl_->message_version());
+ FinishOperationResponse response;
impl_->FinishOperation(request, &response);
hidl_vec<KeyParameter> resultParams;
@@ -602,10 +602,10 @@ Return<void> TrustyKeymaster4Device::finish(uint64_t operationHandle,
}
Return<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {
- AbortOperationRequest request(impl_->message_version());
+ AbortOperationRequest request;
request.op_handle = operationHandle;
- AbortOperationResponse response(impl_->message_version());
+ AbortOperationResponse response;
impl_->AbortOperation(request, &response);
return legacy_enum_conversion(response.error);
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
index 65eb8549a..aa30707ca 100644
--- a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
@@ -2,6 +2,10 @@
<hal format="hidl">
<name>android.hardware.keymaster</name>
<transport>hwbinder</transport>
- <fqname>@4.0::IKeymasterDevice/default</fqname>
+ <version>4.0</version>
+ <interface>
+ <name>IKeymasterDevice</name>
+ <instance>default</instance>
+ </interface>
</hal>
</manifest>
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
index 0e5144dfb..96eb58422 100644
--- a/trusty/keymaster/4.0/service.cpp
+++ b/trusty/keymaster/4.0/service.cpp
@@ -24,7 +24,7 @@
int main() {
::android::hardware::configureRpcThreadpool(1, true);
auto trustyKeymaster = new keymaster::TrustyKeymaster();
- int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_4);
+ int err = trustyKeymaster->Initialize();
if (err != 0) {
LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
return -1;
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index cf056f091..f32a69ea8 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -14,8 +14,68 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
+// WARNING: Everything listed here will be built on ALL platforms,
+// including x86, the emulator, and the SDK. Modules must be uniquely
+// named (liblights.panda), and must build everywhere, or limit themselves
+// to only building on ARM if they include assembly. Individual makefiles
+// are responsible for having their own logic, for fine-grained control.
+
+// trusty_keymaster is a binary used only for on-device testing. It
+// runs Trusty Keymaster through a basic set of operations with RSA
+// and ECDSA keys.
+cc_binary {
+ name: "trusty_keymaster_tipc",
+ vendor: true,
+ srcs: [
+ "ipc/trusty_keymaster_ipc.cpp",
+ "legacy/trusty_keymaster_device.cpp",
+ "legacy/trusty_keymaster_main.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "libcrypto",
+ "libcutils",
+ "libkeymaster_portable",
+ "libtrusty",
+ "libkeymaster_messages",
+ "libsoftkeymasterdevice",
+ "liblog",
+ ],
+}
+
+// keystore.trusty is the HAL used by keystore on Trusty devices.
+cc_library_shared {
+ name: "keystore.trusty",
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "ipc/trusty_keymaster_ipc.cpp",
+ "legacy/module.cpp",
+ "legacy/trusty_keymaster_device.cpp",
+ ],
+
+ cflags: [
+ "-fvisibility=hidden",
+ "-Wall",
+ "-Werror",
+ ],
+
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "libcrypto",
+ "libkeymaster_messages",
+ "libtrusty",
+ "liblog",
+ "libcutils",
+ ],
+ header_libs: ["libhardware_headers"],
}
cc_binary {
@@ -79,100 +139,3 @@ cc_binary {
vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
}
-
-cc_binary {
- name: "android.hardware.security.keymint-service.trusty",
- relative_install_path: "hw",
- init_rc: ["keymint/android.hardware.security.keymint-service.trusty.rc"],
- vintf_fragments: [
- "keymint/android.hardware.security.keymint-service.trusty.xml",
- ],
- vendor: true,
- cflags: [
- "-Wall",
- "-Wextra",
- ],
- local_include_dirs: [
- "include",
- ],
- srcs: [
- "TrustyKeymaster.cpp",
- "ipc/trusty_keymaster_ipc.cpp",
- "keymint/TrustyKeyMintDevice.cpp",
- "keymint/TrustyKeyMintOperation.cpp",
- "keymint/TrustyRemotelyProvisionedComponentDevice.cpp",
- "keymint/TrustySecureClock.cpp",
- "keymint/TrustySharedSecret.cpp",
- "keymint/service.cpp",
- ],
- shared_libs: [
- "android.hardware.security.keymint-V1-ndk_platform",
- "android.hardware.security.secureclock-V1-ndk_platform",
- "android.hardware.security.sharedsecret-V1-ndk_platform",
- "lib_android_keymaster_keymint_utils",
- "libbase",
- "libbinder_ndk",
- "libhardware",
- "libkeymaster_messages",
- "libkeymint",
- "liblog",
- "libtrusty",
- ],
- required: [
- "android.hardware.hardware_keystore.xml",
- "RemoteProvisioner",
- ],
-}
-
-prebuilt_etc {
- name: "keymaster_soft_attestation_keys.xml",
- vendor: true,
- src: "set_attestation_key/keymaster_soft_attestation_keys.xml",
-}
-
-cc_library {
- name: "libtrusty_ipc",
- vendor: true,
- srcs: ["ipc/trusty_keymaster_ipc.cpp"],
- local_include_dirs: ["include"],
- shared_libs: [
- "libc",
- "libcrypto",
- "liblog",
- "libtrusty",
- "libhardware",
- "libkeymaster_messages",
- "libxml2",
- ],
- export_include_dirs: ["include"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
-
-cc_binary {
- name: "trusty_keymaster_set_attestation_key",
- vendor: true,
-
- srcs: [
- "set_attestation_key/set_attestation_key.cpp",
- "ipc/trusty_keymaster_ipc.cpp",
- ],
-
- local_include_dirs: ["include"],
-
- shared_libs: [
- "libc",
- "libcrypto",
- "liblog",
- "libtrusty",
- "libhardware",
- "libkeymaster_messages",
- "libxml2",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index cdfbd9003..f3ef747f6 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "trusty_keymaster_hal"
-#include <android-base/logging.h>
-
+#include <cutils/log.h>
#include <keymaster/android_keymaster_messages.h>
#include <keymaster/keymaster_configuration.h>
#include <trusty_keymaster/TrustyKeymaster.h>
@@ -24,71 +22,27 @@
namespace keymaster {
-int TrustyKeymaster::Initialize(KmVersion version) {
+int TrustyKeymaster::Initialize() {
int err;
- LOG(INFO) << "Initializing TrustyKeymaster as KmVersion: " << (int)version;
-
err = trusty_keymaster_connect();
if (err) {
- LOG(ERROR) << "Failed to connect to trusty keymaster (1st try)" << err;
+ ALOGE("Failed to connect to trusty keymaster %d", err);
return err;
}
- // Try GetVersion2 first.
- GetVersion2Request versionReq;
- versionReq.max_message_version = MessageVersion(version);
- GetVersion2Response versionRsp = GetVersion2(versionReq);
- if (versionRsp.error != KM_ERROR_OK) {
- LOG(WARNING) << "TA appears not to support GetVersion2, falling back (err = "
- << versionRsp.error << ")";
-
- err = trusty_keymaster_connect();
- if (err) {
- LOG(FATAL) << "Failed to connect to trusty keymaster (2nd try) " << err;
- return err;
- }
-
- GetVersionRequest versionReq;
- GetVersionResponse versionRsp;
- GetVersion(versionReq, &versionRsp);
- if (versionRsp.error != KM_ERROR_OK) {
- LOG(FATAL) << "Failed to get TA version " << versionRsp.error;
- return -1;
- } else {
- keymaster_error_t error;
- message_version_ = NegotiateMessageVersion(versionRsp, &error);
- if (error != KM_ERROR_OK) {
- LOG(FATAL) << "Failed to negotiate message version " << error;
- return -1;
- }
- }
- } else {
- message_version_ = NegotiateMessageVersion(versionReq, versionRsp);
- }
-
- ConfigureRequest req(message_version());
+ ConfigureRequest req;
req.os_version = GetOsVersion();
req.os_patchlevel = GetOsPatchlevel();
- ConfigureResponse rsp(message_version());
+ ConfigureResponse rsp;
Configure(req, &rsp);
if (rsp.error != KM_ERROR_OK) {
- LOG(FATAL) << "Failed to configure keymaster " << rsp.error;
+ ALOGE("Failed to configure keymaster %d", rsp.error);
return -1;
}
- // Set the vendor patchlevel to value retrieved from system property (which
- // requires SELinux permission).
- ConfigureVendorPatchlevelRequest vendor_req(message_version());
- vendor_req.vendor_patchlevel = GetVendorPatchlevel();
- ConfigureVendorPatchlevelResponse vendor_rsp = ConfigureVendorPatchlevel(vendor_req);
- if (vendor_rsp.error != KM_ERROR_OK) {
- LOG(ERROR) << "Failed to configure keymaster vendor patchlevel: " << vendor_rsp.error;
- // Don't fail if this message isn't understood.
- }
-
return 0;
}
@@ -98,12 +52,12 @@ TrustyKeymaster::~TrustyKeymaster() {
trusty_keymaster_disconnect();
}
-static void ForwardCommand(enum keymaster_command command, const KeymasterMessage& req,
+static void ForwardCommand(enum keymaster_command command, const Serializable& req,
KeymasterResponse* rsp) {
keymaster_error_t err;
err = trusty_keymaster_send(command, req, rsp);
if (err != KM_ERROR_OK) {
- LOG(ERROR) << "Cmd " << command << " returned error: " << err;
+ ALOGE("Failed to send cmd %d err: %d", command, err);
rsp->error = err;
}
}
@@ -153,29 +107,14 @@ void TrustyKeymaster::Configure(const ConfigureRequest& request, ConfigureRespon
void TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,
GenerateKeyResponse* response) {
- if (message_version_ < 4) {
- // Pre-KeyMint we need to add TAG_CREATION_DATETIME if not provided by the caller.
- GenerateKeyRequest datedRequest(request.message_version);
- datedRequest.key_description = request.key_description;
-
- if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
- datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
- }
-
- ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
- } else {
- ForwardCommand(KM_GENERATE_KEY, request, response);
- }
-}
+ GenerateKeyRequest datedRequest(request.message_version);
+ datedRequest.key_description = request.key_description;
-void TrustyKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,
- GenerateRkpKeyResponse* response) {
- ForwardCommand(KM_GENERATE_RKP_KEY, request, response);
-}
+ if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+ datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+ }
-void TrustyKeymaster::GenerateCsr(const GenerateCsrRequest& request,
- GenerateCsrResponse* response) {
- ForwardCommand(KM_GENERATE_CSR, request, response);
+ ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
}
void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
@@ -234,49 +173,25 @@ void TrustyKeymaster::AbortOperation(const AbortOperationRequest& request,
}
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
- GetHmacSharingParametersRequest request(message_version());
- GetHmacSharingParametersResponse response(message_version());
+ // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ Buffer request;
+ GetHmacSharingParametersResponse response;
ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
return response;
}
ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
const ComputeSharedHmacRequest& request) {
- ComputeSharedHmacResponse response(message_version());
+ ComputeSharedHmacResponse response;
ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
return response;
}
VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
const VerifyAuthorizationRequest& request) {
- VerifyAuthorizationResponse response(message_version());
+ VerifyAuthorizationResponse response;
ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
return response;
}
-GetVersion2Response TrustyKeymaster::GetVersion2(const GetVersion2Request& request) {
- GetVersion2Response response(message_version());
- ForwardCommand(KM_GET_VERSION_2, request, &response);
- return response;
-}
-
-EarlyBootEndedResponse TrustyKeymaster::EarlyBootEnded() {
- EarlyBootEndedResponse response(message_version());
- ForwardCommand(KM_EARLY_BOOT_ENDED, EarlyBootEndedRequest(message_version()), &response);
- return response;
-}
-
-DeviceLockedResponse TrustyKeymaster::DeviceLocked(const DeviceLockedRequest& request) {
- DeviceLockedResponse response(message_version());
- ForwardCommand(KM_DEVICE_LOCKED, request, &response);
- return response;
-}
-
-ConfigureVendorPatchlevelResponse TrustyKeymaster::ConfigureVendorPatchlevel(
- const ConfigureVendorPatchlevelRequest& request) {
- ConfigureVendorPatchlevelResponse response(message_version());
- ForwardCommand(KM_CONFIGURE_VENDOR_PATCHLEVEL, request, &response);
- return response;
-}
-
} // namespace keymaster
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
deleted file mode 100644
index 8d7ee0066..000000000
--- a/trusty/keymaster/fuzz/Android.bp
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_fuzz {
- name: "trusty_keymaster_fuzzer",
- defaults: ["trusty_fuzzer_defaults"],
- srcs: [":trusty_tipc_fuzzer"],
- cflags: [
- "-DTRUSTY_APP_PORT=\"com.android.trusty.keymaster\"",
- "-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
- "-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
- ],
-
- // The initial corpus for this fuzzer was derived by dumping messages from
- // the `secure_env` emulator interface for cuttlefish while running the
- // keystore2 tests in the emulator.
- corpus: ["corpus/*"],
-}
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5 b/trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5
deleted file mode 100644
index 4a292c1a5..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mx b/trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mx
deleted file mode 100644
index 60301f68d..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mx
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8 b/trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8
deleted file mode 100644
index 9ed8279d6..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHi b/trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHi
deleted file mode 100644
index 69caf3340..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHi
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCR b/trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCR
deleted file mode 100644
index e9d7daf59..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCR
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FN b/trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FN
deleted file mode 100644
index 3ce16c3ee..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FN
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRA b/trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRA
deleted file mode 100644
index c290b523a..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRA
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgH b/trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgH
deleted file mode 100644
index b1fb022cd..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgH
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXq b/trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXq
deleted file mode 100644
index 2f9abcfd8..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXq
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5T b/trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5T
deleted file mode 100644
index 9ed8279d6..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5T
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dO b/trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dO
deleted file mode 100644
index ec374e353..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dO
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60 b/trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60
deleted file mode 100644
index 3ce16c3ee..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5D b/trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5D
deleted file mode 100644
index 1641d95cf..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5D
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1t b/trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1t
deleted file mode 100644
index 96b965e77..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1t
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQK b/trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQK
deleted file mode 100644
index e3a04ae86..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQK
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1 b/trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1
deleted file mode 100644
index 920c5edc7..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmg b/trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmg
deleted file mode 100644
index 49453d4f9..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmg
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksu b/trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksu
deleted file mode 100644
index b0408c675..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksu
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1U b/trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1U
deleted file mode 100644
index 8d77e0eb4..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1U
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4n b/trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4n
deleted file mode 100644
index 8ac5b907c..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4n
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjne b/trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjne
deleted file mode 100644
index 22d5232a1..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjne
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39 b/trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39
deleted file mode 100644
index 8e955c1c8..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrY b/trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrY
deleted file mode 100644
index 16d2121cc..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrY
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsH b/trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsH
deleted file mode 100644
index d5d7f0252..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsH
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIA b/trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIA
deleted file mode 100644
index 23c3ce8f0..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIA
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgr b/trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgr
deleted file mode 100644
index bfee18a35..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgr
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9I b/trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9I
deleted file mode 100644
index 3e446c520..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9I
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-7X098Z b/trusty/keymaster/fuzz/corpus/keymaster-send-7X098Z
deleted file mode 100644
index 5b1c30b0a..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-7X098Z
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4 b/trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4
deleted file mode 100644
index e84183611..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LF b/trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LF
deleted file mode 100644
index 72ba6c4ad..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LF
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxg b/trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxg
deleted file mode 100644
index 5dfd4f8a7..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxg
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoC b/trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoC
deleted file mode 100644
index 992e3f537..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoC
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-PzXetK b/trusty/keymaster/fuzz/corpus/keymaster-send-PzXetK
deleted file mode 100644
index 18506f20d..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-PzXetK
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3D b/trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3D
deleted file mode 100644
index 684525767..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3D
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJW b/trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJW
deleted file mode 100644
index 90df6daad..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJW
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7 b/trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7
deleted file mode 100644
index 79512e44e..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3 b/trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3
deleted file mode 100644
index 1423e645f..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiX b/trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiX
deleted file mode 100644
index 18506f20d..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiX
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rk b/trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rk
deleted file mode 100644
index 9af5af3c6..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rk
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wM b/trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wM
deleted file mode 100644
index e8f79be34..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wM
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHv b/trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHv
deleted file mode 100644
index 3ee543479..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHv
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-jiL5yp b/trusty/keymaster/fuzz/corpus/keymaster-send-jiL5yp
deleted file mode 100644
index 90beb99a9..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-jiL5yp
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxc b/trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxc
deleted file mode 100644
index b2f606dbd..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxc
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2y b/trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2y
deleted file mode 100644
index 77705e7a3..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2y
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-ltPKls b/trusty/keymaster/fuzz/corpus/keymaster-send-ltPKls
deleted file mode 100644
index fb637aada..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-ltPKls
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVP b/trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVP
deleted file mode 100644
index 054a7edd3..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVP
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkT b/trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkT
deleted file mode 100644
index 3ed724671..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkT
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-rhVedc b/trusty/keymaster/fuzz/corpus/keymaster-send-rhVedc
deleted file mode 100644
index bd545f1e7..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-rhVedc
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Ex b/trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Ex
deleted file mode 100644
index 72ee499c7..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Ex
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQ b/trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQ
deleted file mode 100644
index e84183611..000000000
--- a/trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQ
+++ /dev/null
Binary files differ
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
deleted file mode 100644
index 5fd628f3c..000000000
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
-#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
-#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
-
-#include <trusty_keymaster/TrustyKeymaster.h>
-
-namespace aidl::android::hardware::security::keymint::trusty {
-
-using ::keymaster::TrustyKeymaster;
-using ::ndk::ScopedAStatus;
-using secureclock::TimeStampToken;
-using ::std::optional;
-using ::std::shared_ptr;
-using ::std::vector;
-
-class TrustyKeyMintDevice : public BnKeyMintDevice {
- public:
- explicit TrustyKeyMintDevice(shared_ptr<TrustyKeymaster> impl) : impl_(std::move(impl)) {}
- virtual ~TrustyKeyMintDevice() = default;
-
- ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
-
- ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
-
- ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) override;
-
- ScopedAStatus getKeyCharacteristics(const vector<uint8_t>& keyBlob,
- const vector<uint8_t>& clientId,
- const vector<uint8_t>& appData,
- vector<KeyCharacteristics>* characteristics) override;
-
- ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
- const vector<uint8_t>& keyData,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) override;
-
- ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
- const vector<uint8_t>& wrappingKeyBlob,
- const vector<uint8_t>& maskingKey,
- const vector<KeyParameter>& unwrappingParams,
- int64_t passwordSid, int64_t biometricSid,
- KeyCreationResult* creationResult) override;
-
- ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
- const vector<KeyParameter>& upgradeParams,
- vector<uint8_t>* keyBlob) override;
-
- ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
- ScopedAStatus deleteAllKeys() override;
- ScopedAStatus destroyAttestationIds() override;
-
- ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
- const vector<KeyParameter>& params,
- const optional<HardwareAuthToken>& authToken, BeginResult* result) override;
-
- ScopedAStatus deviceLocked(bool passwordOnly,
- const optional<TimeStampToken>& timestampToken) override;
- ScopedAStatus earlyBootEnded() override;
-
- ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
- std::vector<uint8_t>* ephemeralKeyBlob) override;
-
- protected:
- std::shared_ptr<TrustyKeymaster> impl_;
- SecurityLevel securityLevel_;
-};
-
-} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h
deleted file mode 100644
index 65fd2f5b8..000000000
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
-#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
-
-#include <trusty_keymaster/TrustyKeymaster.h>
-
-#include <hardware/keymaster_defs.h>
-
-namespace aidl::android::hardware::security::keymint {
-
-using ::keymaster::TrustyKeymaster;
-using ::ndk::ScopedAStatus;
-using secureclock::TimeStampToken;
-using std::optional;
-using std::shared_ptr;
-using std::string;
-using std::vector;
-
-class TrustyKeyMintOperation : public BnKeyMintOperation {
- public:
- explicit TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,
- keymaster_operation_handle_t opHandle);
- virtual ~TrustyKeyMintOperation();
-
- ScopedAStatus updateAad(const vector<uint8_t>& input,
- const optional<HardwareAuthToken>& authToken,
- const optional<TimeStampToken>& timestampToken) override;
-
- ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
- const optional<TimeStampToken>& timestampToken,
- vector<uint8_t>* output) override;
-
- ScopedAStatus finish(const optional<vector<uint8_t>>& input, //
- const optional<vector<uint8_t>>& signature, //
- const optional<HardwareAuthToken>& authToken, //
- const optional<TimeStampToken>& timestampToken,
- const optional<vector<uint8_t>>& confirmationToken,
- vector<uint8_t>* output) override;
-
- ScopedAStatus abort() override;
-
- protected:
- std::shared_ptr<TrustyKeymaster> impl_;
- keymaster_operation_handle_t opHandle_;
-};
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index f80e02f37..030b64598 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -25,7 +25,7 @@ class TrustyKeymaster {
public:
TrustyKeymaster();
~TrustyKeymaster();
- int Initialize(KmVersion version);
+ int Initialize();
void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);
void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
SupportedAlgorithmsResponse* response);
@@ -42,8 +42,6 @@ class TrustyKeymaster {
void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
void Configure(const ConfigureRequest& request, ConfigureResponse* response);
void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
- void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
- void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
GetKeyCharacteristicsResponse* response);
void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -61,16 +59,6 @@ class TrustyKeymaster {
GetHmacSharingParametersResponse GetHmacSharingParameters();
ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
- GetVersion2Response GetVersion2(const GetVersion2Request& request);
- EarlyBootEndedResponse EarlyBootEnded();
- DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
- ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
- const ConfigureVendorPatchlevelRequest& request);
-
- uint32_t message_version() const { return message_version_; }
-
- private:
- uint32_t message_version_;
};
} // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
deleted file mode 100644
index d544b51d5..000000000
--- a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
-#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
-#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
-
-#include <trusty_keymaster/TrustyKeymaster.h>
-
-namespace aidl::android::hardware::security::keymint::trusty {
-
-using ::keymaster::TrustyKeymaster;
-using ::ndk::ScopedAStatus;
-using ::std::shared_ptr;
-
-class TrustyRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
- public:
- explicit TrustyRemotelyProvisionedComponentDevice(shared_ptr<TrustyKeymaster> impl)
- : impl_(std::move(impl)) {}
- virtual ~TrustyRemotelyProvisionedComponentDevice() = default;
-
- ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
-
- ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
- std::vector<uint8_t>* privateKeyHandle) override;
-
- ScopedAStatus generateCertificateRequest(bool testMode,
- const std::vector<MacedPublicKey>& keysToSign,
- const std::vector<uint8_t>& endpointEncCertChain,
- const std::vector<uint8_t>& challenge,
- DeviceInfo* deviceInfo, ProtectedData* protectedData,
- std::vector<uint8_t>* keysToSignMac) override;
-
- private:
- std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
-};
-
-} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h b/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h
deleted file mode 100644
index f077b276f..000000000
--- a/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/secureclock/BnSecureClock.h>
-#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
-#include <aidl/android/hardware/security/secureclock/Timestamp.h>
-
-#include <trusty_keymaster/TrustyKeymaster.h>
-
-namespace aidl::android::hardware::security::secureclock::trusty {
-
-class TrustySecureClock : public BnSecureClock {
- public:
- explicit TrustySecureClock(std::shared_ptr<::keymaster::TrustyKeymaster> impl)
- : impl_(std::move(impl)) {}
- ~TrustySecureClock() = default;
- ::ndk::ScopedAStatus generateTimeStamp(int64_t challenge, TimeStampToken* token) override;
-
- private:
- std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
-};
-
-} // namespace aidl::android::hardware::security::secureclock::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h b/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h
deleted file mode 100644
index 946f57e9d..000000000
--- a/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
-#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
-
-#include <trusty_keymaster/TrustyKeymaster.h>
-
-namespace aidl::android::hardware::security::sharedsecret::trusty {
-
-class TrustySharedSecret : public BnSharedSecret {
- public:
- explicit TrustySharedSecret(std::shared_ptr<::keymaster::TrustyKeymaster> impl)
- : impl_(std::move(impl)) {}
- ~TrustySharedSecret() = default;
-
- ::ndk::ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;
- ::ndk::ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,
- std::vector<uint8_t>* sharingCheck) override;
-
- private:
- std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
-};
-} // namespace aidl::android::hardware::security::sharedsecret::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index fa475ae90..13e672573 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -53,27 +53,6 @@ enum keymaster_command : uint32_t {
KM_DELETE_ALL_KEYS = (23 << KEYMASTER_REQ_SHIFT),
KM_DESTROY_ATTESTATION_IDS = (24 << KEYMASTER_REQ_SHIFT),
KM_IMPORT_WRAPPED_KEY = (25 << KEYMASTER_REQ_SHIFT),
- KM_GET_VERSION_2 = (28 << KEYMASTER_REQ_SHIFT),
- KM_EARLY_BOOT_ENDED = (29 << KEYMASTER_REQ_SHIFT),
- KM_DEVICE_LOCKED = (30 << KEYMASTER_REQ_SHIFT),
- KM_GENERATE_RKP_KEY = (31 << KEYMASTER_REQ_SHIFT),
- KM_GENERATE_CSR = (32 << KEYMASTER_REQ_SHIFT),
- KM_CONFIGURE_VENDOR_PATCHLEVEL = (33 << KEYMASTER_REQ_SHIFT),
-
- // Bootloader/provisioning calls.
- KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
- KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT),
- KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT),
- KM_ATAP_GET_CA_REQUEST = (0x4000 << KEYMASTER_REQ_SHIFT),
- KM_ATAP_SET_CA_RESPONSE_BEGIN = (0x5000 << KEYMASTER_REQ_SHIFT),
- KM_ATAP_SET_CA_RESPONSE_UPDATE = (0x6000 << KEYMASTER_REQ_SHIFT),
- KM_ATAP_SET_CA_RESPONSE_FINISH = (0x7000 << KEYMASTER_REQ_SHIFT),
- KM_ATAP_READ_UUID = (0x8000 << KEYMASTER_REQ_SHIFT),
- KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
- KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
- KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
- KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
- KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 2d4400989..0956fe6b8 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -25,8 +25,6 @@
#include <unistd.h>
#include <algorithm>
-#include <variant>
-#include <vector>
#include <log/log.h>
#include <trusty/tipc.h>
@@ -48,27 +46,8 @@ int trusty_keymaster_connect() {
return 0;
}
-class VectorEraser {
- public:
- VectorEraser(std::vector<uint8_t>* v) : _v(v) {}
- ~VectorEraser() {
- if (_v) {
- std::fill(const_cast<volatile uint8_t*>(_v->data()),
- const_cast<volatile uint8_t*>(_v->data() + _v->size()), 0);
- }
- }
- void disarm() { _v = nullptr; }
- VectorEraser(const VectorEraser&) = delete;
- VectorEraser& operator=(const VectorEraser&) = delete;
- VectorEraser(VectorEraser&& other) = delete;
- VectorEraser& operator=(VectorEraser&&) = delete;
-
- private:
- std::vector<uint8_t>* _v;
-};
-
-std::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, void* in,
- uint32_t in_size) {
+int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size) {
if (handle_ < 0) {
ALOGE("not connected\n");
return -EINVAL;
@@ -91,38 +70,15 @@ std::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, vo
ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
return -errno;
}
-
- std::vector<uint8_t> out(TRUSTY_KEYMASTER_RECV_BUF_SIZE);
- VectorEraser out_eraser(&out);
- uint8_t* write_pos = out.data();
- uint8_t* out_end = out.data() + out.size();
-
+ size_t out_max_size = *out_size;
+ *out_size = 0;
struct iovec iov[2];
struct keymaster_message header;
iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
while (true) {
- if (out_end - write_pos < KEYMASTER_MAX_BUFFER_LENGTH) {
- // In stead of using std::vector.resize(), allocate a new one to have chance
- // at zeroing the old buffer.
- std::vector<uint8_t> new_out(out.size() + KEYMASTER_MAX_BUFFER_LENGTH);
- // After the swap below this erases the old out buffer.
- VectorEraser new_out_eraser(&new_out);
- std::copy(out.data(), write_pos, new_out.begin());
-
- auto write_offset = write_pos - out.data();
-
- std::swap(new_out, out);
-
- write_pos = out.data() + write_offset;
- out_end = out.data() + out.size();
- }
- size_t buffer_size = 0;
- if (__builtin_sub_overflow(reinterpret_cast<uintptr_t>(out_end),
- reinterpret_cast<uintptr_t>(write_pos), &buffer_size)) {
- return -EOVERFLOW;
- }
- iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};
-
+ iov[1] = {.iov_base = out + *out_size,
+ .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH,
+ out_max_size - *out_size)};
rc = readv(handle_, iov, 2);
if (rc < 0) {
ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
@@ -139,36 +95,13 @@ std::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, vo
ALOGE("invalid command (%d)", header.cmd);
return -EINVAL;
}
- write_pos += ((size_t)rc - sizeof(struct keymaster_message));
+ *out_size += ((size_t)rc - sizeof(struct keymaster_message));
if (header.cmd & KEYMASTER_STOP_BIT) {
break;
}
}
- out.resize(write_pos - out.data());
- out_eraser.disarm();
- return out;
-}
-
-int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
- uint32_t* out_size) {
- auto result = trusty_keymaster_call_2(cmd, in, in_size);
- if (auto out_buffer = std::get_if<std::vector<uint8_t>>(&result)) {
- if (out_buffer->size() <= *out_size) {
- std::copy(out_buffer->begin(), out_buffer->end(), out);
- std::fill(const_cast<volatile uint8_t*>(&*out_buffer->begin()),
- const_cast<volatile uint8_t*>(&*out_buffer->end()), 0);
-
- *out_size = out_buffer->size();
- return 0;
- } else {
- ALOGE("Message was to large (%zu) for the provided buffer (%u)", out_buffer->size(),
- *out_size);
- return -EMSGSIZE;
- }
- } else {
- return std::get<int>(result);
- }
+ return rc;
}
void trusty_keymaster_disconnect() {
@@ -222,27 +155,28 @@ keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Seria
req.Serialize(send_buf, send_buf + req_size);
// Send it
- auto response = trusty_keymaster_call_2(command, send_buf, req_size);
- if (auto response_buffer = std::get_if<std::vector<uint8_t>>(&response)) {
- keymaster::Eraser response_buffer_erasor(response_buffer->data(), response_buffer->size());
- ALOGV("Received %zu byte response\n", response_buffer->size());
-
- const uint8_t* p = response_buffer->data();
- if (!rsp->Deserialize(&p, p + response_buffer->size())) {
- ALOGE("Error deserializing response of size %zu\n", response_buffer->size());
- return KM_ERROR_UNKNOWN_ERROR;
- } else if (rsp->error != KM_ERROR_OK) {
- ALOGE("Response of size %zu contained error code %d\n", response_buffer->size(),
- (int)rsp->error);
- }
- return rsp->error;
- } else {
- auto rc = std::get<int>(response);
+ uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE];
+ keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE);
+ uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE;
+ int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
+ if (rc < 0) {
// Reset the connection on tipc error
trusty_keymaster_disconnect();
trusty_keymaster_connect();
ALOGE("tipc error: %d\n", rc);
// TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
return translate_error(rc);
+ } else {
+ ALOGV("Received %d byte response\n", rsp_size);
+ }
+
+ const uint8_t* p = recv_buf;
+ if (!rsp->Deserialize(&p, p + rsp_size)) {
+ ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
+ return KM_ERROR_UNKNOWN_ERROR;
+ } else if (rsp->error != KM_ERROR_OK) {
+ ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
+ return rsp->error;
}
+ return rsp->error;
}
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
deleted file mode 100644
index 5f8524b01..000000000
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
-
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <trusty_keymaster/TrustyKeyMintDevice.h>
-
-#define TAG TrustyKeyMintDevice
-#include <android-base/logging.h>
-
-#include <keymaster/android_keymaster_messages.h>
-#include <keymaster/authorization_set.h>
-
-#include <KeyMintUtils.h>
-
-#include <trusty_keymaster/TrustyKeyMintOperation.h>
-
-namespace aidl::android::hardware::security::keymint::trusty {
-
-using keymaster::KeymasterBlob;
-using keymaster::KeymasterKeyBlob;
-using keymaster::TAG_APPLICATION_DATA;
-using keymaster::TAG_APPLICATION_ID;
-using keymaster::TAG_AUTH_TOKEN;
-using km_utils::authToken2AidlVec;
-using km_utils::kmBlob2vector;
-using km_utils::kmError2ScopedAStatus;
-using km_utils::kmParam2Aidl;
-using km_utils::KmParamSet;
-using km_utils::kmParamSet2Aidl;
-using km_utils::legacy_enum_conversion;
-
-namespace {
-
-auto kSecurityLevel = SecurityLevel::TRUSTED_ENVIRONMENT;
-
-KeyCharacteristics convertAuthSet(SecurityLevel securityLevel,
- const keymaster::AuthorizationSet& authorizations) {
- KeyCharacteristics retval{securityLevel, {}};
- std::transform(authorizations.begin(), authorizations.end(),
- std::back_inserter(retval.authorizations), kmParam2Aidl);
- return retval;
-}
-
-vector<KeyCharacteristics> convertKeyCharacteristics(const keymaster::AuthorizationSet& sw_enforced,
- const keymaster::AuthorizationSet& hw_enforced,
- bool includeKeystoreEnforced = true) {
- KeyCharacteristics keyMintEnforced = convertAuthSet(kSecurityLevel, hw_enforced);
- KeyCharacteristics keystoreEnforced = convertAuthSet(SecurityLevel::KEYSTORE, sw_enforced);
-
- vector<KeyCharacteristics> retval;
- retval.reserve(2);
-
- if (!keyMintEnforced.authorizations.empty()) retval.push_back(std::move(keyMintEnforced));
- if (includeKeystoreEnforced && !keystoreEnforced.authorizations.empty()) {
- retval.push_back(std::move(keystoreEnforced));
- }
-
- return retval;
-}
-
-Certificate convertCertificate(const keymaster_blob_t& cert) {
- return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};
-}
-
-vector<Certificate> convertCertificateChain(const keymaster::CertificateChain& chain) {
- vector<Certificate> retval;
- std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate);
- return retval;
-}
-
-void addClientAndAppData(const vector<uint8_t>& clientId, const vector<uint8_t>& appData,
- ::keymaster::AuthorizationSet* params) {
- params->Clear();
- if (clientId.size()) params->push_back(TAG_APPLICATION_ID, clientId.data(), clientId.size());
- if (appData.size()) params->push_back(TAG_APPLICATION_DATA, appData.data(), appData.size());
-}
-
-} // namespace
-
-ScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
- info->versionNumber = 1;
- info->securityLevel = kSecurityLevel;
- info->keyMintName = "TrustyKeyMintDevice";
- info->keyMintAuthorName = "Google";
- info->timestampTokenRequired = false;
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
- if (data.size() == 0) return ScopedAStatus::ok();
- if (data.size() > 2048) {
- LOG(DEBUG) << "Too-large entropy update of " << data.size() << " bytes.";
- return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
- }
-
- keymaster::AddEntropyRequest request(impl_->message_version());
- request.random_data.Reinitialize(data.data(), data.size());
-
- keymaster::AddEntropyResponse response(impl_->message_version());
- impl_->AddRngEntropy(request, &response);
-
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus TrustyKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) {
- keymaster::GenerateKeyRequest request(impl_->message_version());
- request.key_description.Reinitialize(KmParamSet(keyParams));
- if (attestationKey) {
- request.attestation_signing_key_blob =
- KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
- request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
- request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
- attestationKey->issuerSubjectName.size());
- }
-
- keymaster::GenerateKeyResponse response(impl_->message_version());
- impl_->GenerateKey(request, &response);
-
- if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
-
- creationResult->keyBlob = kmBlob2vector(response.key_blob);
- creationResult->keyCharacteristics =
- convertKeyCharacteristics(response.unenforced, response.enforced);
- creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::getKeyCharacteristics(
- const vector<uint8_t>& keyBlob,
- const vector<uint8_t>& clientId, //
- const vector<uint8_t>& appData, //
- vector<KeyCharacteristics>* characteristics) {
- keymaster::GetKeyCharacteristicsRequest request(impl_->message_version());
- request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- addClientAndAppData(clientId, appData, &request.additional_params);
-
- keymaster::GetKeyCharacteristicsResponse response(impl_->message_version());
- impl_->GetKeyCharacteristics(request, &response);
-
- if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
-
- *characteristics = convertKeyCharacteristics(response.unenforced, response.enforced,
- false /* includeKeystoreEnforced */);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
- KeyFormat keyFormat, const vector<uint8_t>& keyData,
- const optional<AttestationKey>& attestationKey,
- KeyCreationResult* creationResult) {
- keymaster::ImportKeyRequest request(impl_->message_version());
- request.key_description.Reinitialize(KmParamSet(keyParams));
- request.key_format = legacy_enum_conversion(keyFormat);
- request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
- if (attestationKey) {
- request.attestation_signing_key_blob =
- KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
- request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
- request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
- attestationKey->issuerSubjectName.size());
- }
-
- keymaster::ImportKeyResponse response(impl_->message_version());
- impl_->ImportKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- creationResult->keyBlob = kmBlob2vector(response.key_blob);
- creationResult->keyCharacteristics =
- convertKeyCharacteristics(response.unenforced, response.enforced);
- creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData,
- const vector<uint8_t>& wrappingKeyBlob, //
- const vector<uint8_t>& maskingKey,
- const vector<KeyParameter>& unwrappingParams,
- int64_t passwordSid, //
- int64_t biometricSid,
- KeyCreationResult* creationResult) {
- keymaster::ImportWrappedKeyRequest request(impl_->message_version());
- request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
- request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
- request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
- request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
- request.password_sid = static_cast<uint64_t>(passwordSid);
- request.biometric_sid = static_cast<uint64_t>(biometricSid);
-
- keymaster::ImportWrappedKeyResponse response(impl_->message_version());
- impl_->ImportWrappedKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- creationResult->keyBlob = kmBlob2vector(response.key_blob);
- creationResult->keyCharacteristics =
- convertKeyCharacteristics(response.unenforced, response.enforced);
- creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
- const vector<KeyParameter>& upgradeParams,
- vector<uint8_t>* keyBlob) {
- keymaster::UpgradeKeyRequest request(impl_->message_version());
- request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
- request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
-
- keymaster::UpgradeKeyResponse response(impl_->message_version());
- impl_->UpgradeKey(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- *keyBlob = kmBlob2vector(response.upgraded_key);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {
- keymaster::DeleteKeyRequest request(impl_->message_version());
- request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
-
- keymaster::DeleteKeyResponse response(impl_->message_version());
- impl_->DeleteKey(request, &response);
-
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus TrustyKeyMintDevice::deleteAllKeys() {
- // There's nothing to be done to delete software key blobs.
- keymaster::DeleteAllKeysRequest request(impl_->message_version());
- keymaster::DeleteAllKeysResponse response(impl_->message_version());
- impl_->DeleteAllKeys(request, &response);
-
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
- const vector<KeyParameter>& params,
- const optional<HardwareAuthToken>& authToken,
- BeginResult* result) {
- keymaster::BeginOperationRequest request(impl_->message_version());
- request.purpose = legacy_enum_conversion(purpose);
- request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- request.additional_params.Reinitialize(KmParamSet(params));
-
- vector<uint8_t> vector_token = authToken2AidlVec(authToken);
- request.additional_params.push_back(
- TAG_AUTH_TOKEN, reinterpret_cast<uint8_t*>(vector_token.data()), vector_token.size());
-
- keymaster::BeginOperationResponse response(impl_->message_version());
- impl_->BeginOperation(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return kmError2ScopedAStatus(response.error);
- }
-
- result->params = kmParamSet2Aidl(response.output_params);
- result->challenge = response.op_handle;
- result->operation = ndk::SharedRefBase::make<TrustyKeyMintOperation>(impl_, response.op_handle);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::deviceLocked(
- bool passwordOnly, const std::optional<secureclock::TimeStampToken>& timestampToken) {
- keymaster::DeviceLockedRequest request(impl_->message_version());
- request.passwordOnly = passwordOnly;
- if (timestampToken.has_value()) {
- request.token.challenge = timestampToken->challenge;
- request.token.mac = {timestampToken->mac.data(), timestampToken->mac.size()};
- request.token.timestamp = timestampToken->timestamp.milliSeconds;
- }
- keymaster::DeviceLockedResponse response = impl_->DeviceLocked(request);
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus TrustyKeyMintDevice::earlyBootEnded() {
- keymaster::EarlyBootEndedResponse response = impl_->EarlyBootEnded();
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
- const std::vector<uint8_t>& storageKeyBlob, std::vector<uint8_t>* ephemeralKeyBlob) {
- keymaster::ExportKeyRequest request(impl_->message_version());
- request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
- request.key_format = KM_KEY_FORMAT_RAW;
-
- keymaster::ExportKeyResponse response(impl_->message_version());
- impl_->ExportKey(request, &response);
-
- if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
- if (response.key_data) {
- *ephemeralKeyBlob = {response.key_data, response.key_data + response.key_data_length};
- }
- return ScopedAStatus::ok();
-}
-
-} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
deleted file mode 100644
index 9440724da..000000000
--- a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <trusty_keymaster/TrustyKeyMintOperation.h>
-
-#define TAG TrustyKeyMintOperation
-#include <android-base/logging.h>
-
-#include <aidl/android/hardware/security/keymint/ErrorCode.h>
-#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
-
-#include <KeyMintUtils.h>
-#include <keymaster/android_keymaster.h>
-#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
-
-namespace aidl::android::hardware::security::keymint {
-
-using ::keymaster::AbortOperationRequest;
-using ::keymaster::AbortOperationResponse;
-using ::keymaster::FinishOperationRequest;
-using ::keymaster::FinishOperationResponse;
-using ::keymaster::TAG_ASSOCIATED_DATA;
-using ::keymaster::TAG_AUTH_TOKEN;
-using ::keymaster::TAG_CONFIRMATION_TOKEN;
-using ::keymaster::UpdateOperationRequest;
-using ::keymaster::UpdateOperationResponse;
-using km_utils::authToken2AidlVec;
-using km_utils::kmError2ScopedAStatus;
-using secureclock::TimeStampToken;
-
-TrustyKeyMintOperation::TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,
- keymaster_operation_handle_t opHandle)
- : impl_(std::move(implementation)), opHandle_(opHandle) {}
-
-TrustyKeyMintOperation::~TrustyKeyMintOperation() {
- if (opHandle_ != 0) {
- abort();
- }
-}
-
-ScopedAStatus TrustyKeyMintOperation::updateAad(
- const vector<uint8_t>& input, const optional<HardwareAuthToken>& /* authToken */,
- const optional<TimeStampToken>& /* timestampToken */) {
- UpdateOperationRequest request(impl_->message_version());
- request.op_handle = opHandle_;
- request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(), input.size());
-
- UpdateOperationResponse response(impl_->message_version());
- impl_->UpdateOperation(request, &response);
-
- return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus TrustyKeyMintOperation::update(const vector<uint8_t>& input,
- const optional<HardwareAuthToken>& authToken,
- const optional<TimeStampToken>& /* timestampToken */,
- vector<uint8_t>* output) {
- if (!output) return kmError2ScopedAStatus(KM_ERROR_OUTPUT_PARAMETER_NULL);
-
- UpdateOperationRequest request(impl_->message_version());
- request.op_handle = opHandle_;
- if (authToken) {
- auto tokenAsVec(authToken2AidlVec(*authToken));
- request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
- }
-
- size_t serialized_size = request.SerializedSize();
- if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
- return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
- }
-
- const uint8_t* input_pos = input.data();
- const uint8_t* input_end = input.data() + input.size();
- const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;
- output->clear();
-
- while (input_pos < input_end) {
- size_t to_send = std::min(max_chunk_size, static_cast<size_t>(input_end - input_pos));
- LOG(DEBUG) << "update: Sending " << to_send << " of " << (input_end - input_pos)
- << " bytes";
- request.input.Reinitialize(input_pos, to_send);
-
- UpdateOperationResponse response(impl_->message_version());
- impl_->UpdateOperation(request, &response);
- if (response.error != KM_ERROR_OK) {
- opHandle_ = 0; // Operation has ended, the handle is invalid. This saves an abort().
- return kmError2ScopedAStatus(response.error);
- }
-
- input_pos += response.input_consumed;
- output->insert(output->end(), response.output.begin(), response.output.end());
- }
-
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintOperation::finish(const optional<vector<uint8_t>>& input, //
- const optional<vector<uint8_t>>& signature, //
- const optional<HardwareAuthToken>& authToken,
- const optional<TimeStampToken>& /* timestampToken */,
- const optional<vector<uint8_t>>& confirmationToken,
- vector<uint8_t>* output) {
- if (!output) {
- return ScopedAStatus(AStatus_fromServiceSpecificError(
- static_cast<int32_t>(ErrorCode::OUTPUT_PARAMETER_NULL)));
- }
- output->clear();
-
- FinishOperationRequest request(impl_->message_version());
-
- if (authToken) {
- auto tokenAsVec(authToken2AidlVec(*authToken));
- request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
- }
- if (confirmationToken) {
- request.additional_params.push_back(TAG_CONFIRMATION_TOKEN, confirmationToken->data(),
- confirmationToken->size());
- }
-
- request.op_handle = opHandle_;
- if (signature) request.signature.Reinitialize(signature->data(), signature->size());
- size_t serialized_size = request.SerializedSize();
- if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
- return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
- }
-
- if (input) {
- const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;
-
- if (input->size() > max_chunk_size) {
- LOG(DEBUG) << "Sending an update to process finish() data";
- // Use update to process all but the last max_chunk_size bytes.
- auto result = update({input->begin(), input->end() - max_chunk_size}, authToken,
- std::nullopt /* timestampToken */, output);
- if (!result.isOk()) return result;
-
- // Process the last max_chunk_size with finish.
- request.input.Reinitialize(input->data() + (input->size() - max_chunk_size),
- max_chunk_size);
- } else {
- request.input.Reinitialize(input->data(), input->size());
- }
- }
-
- FinishOperationResponse response(impl_->message_version());
- impl_->FinishOperation(request, &response);
- opHandle_ = 0;
-
- if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
-
- *output = {response.output.begin(), response.output.end()};
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintOperation::abort() {
- AbortOperationRequest request(impl_->message_version());
- request.op_handle = opHandle_;
-
- AbortOperationResponse response(impl_->message_version());
- impl_->AbortOperation(request, &response);
- opHandle_ = 0;
-
- return kmError2ScopedAStatus(response.error);
-}
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
deleted file mode 100644
index 5664829d8..000000000
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
-
-#include <assert.h>
-#include <variant>
-
-#include <KeyMintUtils.h>
-#include <keymaster/keymaster_configuration.h>
-
-#include <trusty_keymaster/TrustyKeyMintDevice.h>
-
-namespace aidl::android::hardware::security::keymint::trusty {
-
-using keymaster::GenerateCsrRequest;
-using keymaster::GenerateCsrResponse;
-using keymaster::GenerateRkpKeyRequest;
-using keymaster::GenerateRkpKeyResponse;
-using keymaster::KeymasterBlob;
-using ::std::string;
-using ::std::unique_ptr;
-using ::std::vector;
-using bytevec = ::std::vector<uint8_t>;
-
-namespace {
-
-constexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED;
-
-struct AStatusDeleter {
- void operator()(AStatus* p) { AStatus_delete(p); }
-};
-
-class Status {
- public:
- Status() : status_(AStatus_newOk()) {}
- Status(int32_t errCode, const std::string& errMsg)
- : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}
- explicit Status(const std::string& errMsg)
- : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}
- explicit Status(AStatus* status) : status_(status ? status : AStatus_newOk()) {}
-
- Status(Status&&) = default;
- Status(const Status&) = delete;
-
- operator ::ndk::ScopedAStatus() && { // NOLINT(google-explicit-constructor)
- return ndk::ScopedAStatus(status_.release());
- }
-
- bool isOk() const { return AStatus_isOk(status_.get()); }
-
- const char* getMessage() const { return AStatus_getMessage(status_.get()); }
-
- private:
- std::unique_ptr<AStatus, AStatusDeleter> status_;
-};
-
-} // namespace
-
-ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
- info->versionNumber = 1;
- info->rpcAuthorName = "Google";
- info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
- bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) {
- GenerateRkpKeyRequest request(impl_->message_version());
- request.test_mode = testMode;
- GenerateRkpKeyResponse response(impl_->message_version());
- impl_->GenerateRkpKey(request, &response);
- if (response.error != KM_ERROR_OK) {
- return Status(-static_cast<int32_t>(response.error), "Failure in key generation.");
- }
-
- macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
- *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
- return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequest(
- bool testMode, const vector<MacedPublicKey>& keysToSign,
- const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
- ProtectedData* protectedData, bytevec* keysToSignMac) {
- GenerateCsrRequest request(impl_->message_version());
- request.test_mode = testMode;
- request.num_keys = keysToSign.size();
- request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
- for (size_t i = 0; i < keysToSign.size(); i++) {
- request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
- }
- request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());
- request.SetChallenge(challenge.data(), challenge.size());
- GenerateCsrResponse response(impl_->message_version());
- impl_->GenerateCsr(request, &response);
-
- if (response.error != KM_ERROR_OK) {
- return Status(-static_cast<int32_t>(response.error), "Failure in CSR Generation.");
- }
- deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
- protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);
- *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
- return ScopedAStatus::ok();
-}
-
-} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustySecureClock.cpp b/trusty/keymaster/keymint/TrustySecureClock.cpp
deleted file mode 100644
index fed5420ae..000000000
--- a/trusty/keymaster/keymint/TrustySecureClock.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <trusty_keymaster/TrustySecureClock.h>
-
-#include <aidl/android/hardware/security/keymint/ErrorCode.h>
-
-#include <KeyMintUtils.h>
-
-namespace aidl::android::hardware::security::secureclock::trusty {
-
-using keymint::km_utils::kmBlob2vector;
-using keymint::km_utils::kmError2ScopedAStatus;
-
-::ndk::ScopedAStatus TrustySecureClock::generateTimeStamp(int64_t challenge,
- TimeStampToken* token) {
- keymaster::VerifyAuthorizationRequest request(impl_->message_version());
- request.challenge = challenge;
-
- auto response = impl_->VerifyAuthorization(request);
- if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
-
- token->challenge = response.token.challenge;
- token->timestamp.milliSeconds = static_cast<int64_t>(response.token.timestamp);
- token->mac = kmBlob2vector(response.token.mac);
- return ::ndk::ScopedAStatus::ok();
-}
-
-} // namespace aidl::android::hardware::security::secureclock::trusty
diff --git a/trusty/keymaster/keymint/TrustySharedSecret.cpp b/trusty/keymaster/keymint/TrustySharedSecret.cpp
deleted file mode 100644
index 81091688a..000000000
--- a/trusty/keymaster/keymint/TrustySharedSecret.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2020, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <trusty_keymaster/TrustySharedSecret.h>
-
-#include <aidl/android/hardware/security/keymint/ErrorCode.h>
-#include <keymaster/android_keymaster.h>
-#include "KeyMintUtils.h"
-
-namespace aidl::android::hardware::security::sharedsecret::trusty {
-
-using keymint::km_utils::kmBlob2vector;
-using keymint::km_utils::kmError2ScopedAStatus;
-
-::ndk::ScopedAStatus TrustySharedSecret::getSharedSecretParameters(SharedSecretParameters* params) {
- auto response = impl_->GetHmacSharingParameters();
- params->seed = kmBlob2vector(response.params.seed);
- params->nonce = {std::begin(response.params.nonce), std::end(response.params.nonce)};
- return kmError2ScopedAStatus(response.error);
-}
-
-::ndk::ScopedAStatus TrustySharedSecret::computeSharedSecret(
- const std::vector<SharedSecretParameters>& params, std::vector<uint8_t>* sharingCheck) {
- keymaster::ComputeSharedHmacRequest request(impl_->message_version());
- request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
- request.params_array.num_params = params.size();
- for (size_t i = 0; i < params.size(); ++i) {
- request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};
- if (sizeof(request.params_array.params_array[i].nonce) != params[i].nonce.size()) {
- return kmError2ScopedAStatus(KM_ERROR_INVALID_ARGUMENT);
- }
- memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
- params[i].nonce.size());
- }
-
- auto response = impl_->ComputeSharedHmac(request);
- if (response.error == KM_ERROR_OK) *sharingCheck = kmBlob2vector(response.sharing_check);
- return kmError2ScopedAStatus(response.error);
-}
-
-} // namespace aidl::android::hardware::security::sharedsecret::trusty
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc
deleted file mode 100644
index 389af412f..000000000
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.keymint-trusty /vendor/bin/hw/android.hardware.security.keymint-service.trusty
- class early_hal
- user nobody
- group drmrpc
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
deleted file mode 100644
index 7ca50507d..000000000
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<manifest version="1.0" type="device">
- <hal format="aidl">
- <name>android.hardware.security.keymint</name>
- <fqname>IKeyMintDevice/default</fqname>
- </hal>
- <hal format="aidl">
- <name>android.hardware.security.secureclock</name>
- <fqname>ISecureClock/default</fqname>
- </hal>
- <hal format="aidl">
- <name>android.hardware.security.sharedsecret</name>
- <fqname>ISharedSecret/default</fqname>
- </hal>
- <hal format="aidl">
- <name>android.hardware.security.keymint</name>
- <fqname>IRemotelyProvisionedComponent/default</fqname>
- </hal>
-</manifest>
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
deleted file mode 100644
index 4060278d4..000000000
--- a/trusty/keymaster/keymint/service.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-service.trusty"
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-
-#include <trusty_keymaster/TrustyKeyMintDevice.h>
-#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
-#include <trusty_keymaster/TrustySecureClock.h>
-#include <trusty_keymaster/TrustySharedSecret.h>
-
-using aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;
-using aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;
-using aidl::android::hardware::security::secureclock::trusty::TrustySecureClock;
-using aidl::android::hardware::security::sharedsecret::trusty::TrustySharedSecret;
-
-template <typename T, class... Args>
-std::shared_ptr<T> addService(Args&&... args) {
- std::shared_ptr<T> service = std::make_shared<T>(std::forward<Args>(args)...);
- auto instanceName = std::string(T::descriptor) + "/default";
- LOG(ERROR) << "Adding service instance: " << instanceName;
- auto status = AServiceManager_addService(service->asBinder().get(), instanceName.c_str());
- CHECK(status == STATUS_OK) << "Failed to add service " << instanceName;
- return service;
-}
-
-int main() {
- auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
- int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_1);
- if (err != 0) {
- LOG(FATAL) << "Could not initialize TrustyKeymaster for KeyMint (" << err << ")";
- return -1;
- }
-
- // Zero threads seems like a useless pool but below we'll join this thread to it, increasing
- // the pool size to 1.
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
- auto keyMint = addService<TrustyKeyMintDevice>(trustyKeymaster);
- auto secureClock = addService<TrustySecureClock>(trustyKeymaster);
- auto sharedSecret = addService<TrustySharedSecret>(trustyKeymaster);
- auto remotelyProvisionedComponent =
- addService<TrustyRemotelyProvisionedComponentDevice>(trustyKeymaster);
- ABinderProcess_joinThreadPool();
- return EXIT_FAILURE; // should not reach
-}
diff --git a/trusty/keymaster/legacy/Makefile b/trusty/keymaster/legacy/Makefile
new file mode 100644
index 000000000..f57538189
--- /dev/null
+++ b/trusty/keymaster/legacy/Makefile
@@ -0,0 +1,199 @@
+#####
+# Local unit test Makefile
+#
+# This makefile builds and runs the trusty_keymaster unit tests locally on the development
+# machine, not on an Android device.
+#
+# To build and run these tests, one pre-requisite must be manually installed: BoringSSL.
+# This Makefile expects to find BoringSSL in a directory adjacent to $ANDROID_BUILD_TOP.
+# To get and build it, first install the Ninja build tool (e.g. apt-get install
+# ninja-build), then do:
+#
+# cd $ANDROID_BUILD_TOP/..
+# git clone https://boringssl.googlesource.com/boringssl
+# cd boringssl
+# mdkir build
+# cd build
+# cmake -GNinja ..
+# ninja
+#
+# Then return to $ANDROID_BUILD_TOP/system/keymaster and run "make".
+#####
+
+BASE=../../../..
+SUBS=system/core \
+ system/keymaster \
+ hardware/libhardware \
+ external/gtest
+GTEST=$(BASE)/external/gtest
+KM=$(BASE)/system/keymaster
+
+INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
+ -I $(BASE)/libnativehelper/include/nativehelper \
+ -I ../tipc/include \
+ -I $(BASE)/system/keymaster \
+ -I $(GTEST) \
+ -I$(BASE)/../boringssl/include
+
+ifdef USE_CLANG
+CC=/usr/bin/clang
+CXX=/usr/bin/clang
+CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
+COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
+else
+COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
+endif
+
+CPPFLAGS=$(INCLUDES) -g -O0 -MD
+CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \
+ -Wmissing-declarations -ftest-coverage \
+ -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
+ $(COMPILER_SPECIFIC_ARGS)
+LDLIBS=-L$(BASE)/../boringssl/build/crypto -lcrypto -lpthread -lstdc++
+
+CPPSRCS=\
+ $(KM)/aead_mode_operation.cpp \
+ $(KM)/aes_key.cpp \
+ $(KM)/aes_operation.cpp \
+ $(KM)/android_keymaster.cpp \
+ $(KM)/android_keymaster_messages.cpp \
+ $(KM)/android_keymaster_messages_test.cpp \
+ $(KM)/android_keymaster_test.cpp \
+ $(KM)/android_keymaster_test_utils.cpp \
+ $(KM)/android_keymaster_utils.cpp \
+ $(KM)/asymmetric_key.cpp \
+ $(KM)/auth_encrypted_key_blob.cpp \
+ $(KM)/auth_encrypted_key_blob.cpp \
+ $(KM)/authorization_set.cpp \
+ $(KM)/authorization_set_test.cpp \
+ $(KM)/ec_key.cpp \
+ $(KM)/ec_keymaster0_key.cpp \
+ $(KM)/ecdsa_operation.cpp \
+ $(KM)/hmac_key.cpp \
+ $(KM)/hmac_operation.cpp \
+ $(KM)/integrity_assured_key_blob.cpp \
+ $(KM)/key.cpp \
+ $(KM)/key_blob_test.cpp \
+ $(KM)/keymaster0_engine.cpp \
+ $(KM)/logger.cpp \
+ $(KM)/ocb_utils.cpp \
+ $(KM)/openssl_err.cpp \
+ $(KM)/openssl_utils.cpp \
+ $(KM)/operation.cpp \
+ $(KM)/operation_table.cpp \
+ $(KM)/rsa_key.cpp \
+ $(KM)/rsa_keymaster0_key.cpp \
+ $(KM)/rsa_operation.cpp \
+ $(KM)/serializable.cpp \
+ $(KM)/soft_keymaster_context.cpp \
+ $(KM)/symmetric_key.cpp \
+ $(KM)/unencrypted_key_blob.cpp \
+ trusty_keymaster_device.cpp \
+ trusty_keymaster_device_test.cpp
+CCSRCS=$(GTEST)/src/gtest-all.cc
+CSRCS=ocb.c
+
+OBJS=$(CPPSRCS:.cpp=.o) $(CCSRCS:.cc=.o) $(CSRCS:.c=.o)
+DEPS=$(CPPSRCS:.cpp=.d) $(CCSRCS:.cc=.d) $(CSRCS:.c=.d)
+GCDA=$(CPPSRCS:.cpp=.gcda) $(CCSRCS:.cc=.gcda) $(CSRCS:.c=.gcda)
+GCNO=$(CPPSRCS:.cpp=.gcno) $(CCSRCS:.cc=.gcno) $(CSRCS:.c=.gcno)
+
+LINK.o=$(LINK.cc)
+
+BINARIES=trusty_keymaster_device_test
+
+ifdef TRUSTY
+BINARIES += trusty_keymaster_device_test
+endif # TRUSTY
+
+.PHONY: coverage memcheck massif clean run
+
+%.run: %
+ ./$<
+ touch $@
+
+run: $(BINARIES:=.run)
+
+coverage: coverage.info
+ genhtml coverage.info --output-directory coverage
+
+coverage.info: run
+ lcov --capture --directory=. --output-file coverage.info
+
+%.coverage : %
+ $(MAKE) clean && $(MAKE) $<
+ ./$<
+ lcov --capture --directory=. --output-file coverage.info
+ genhtml coverage.info --output-directory coverage
+
+#UNINIT_OPTS=--track-origins=yes
+UNINIT_OPTS=--undef-value-errors=no
+
+MEMCHECK_OPTS=--leak-check=full \
+ --show-reachable=yes \
+ --vgdb=full \
+ $(UNINIT_OPTS) \
+ --error-exitcode=1
+
+MASSIF_OPTS=--tool=massif \
+ --stacks=yes
+
+%.memcheck : %
+ valgrind $(MEMCHECK_OPTS) ./$< && \
+ touch $@
+
+%.massif : %
+ valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
+
+memcheck: $(BINARIES:=.memcheck)
+
+massif: $(BINARIES:=.massif)
+
+trusty_keymaster_device_test: trusty_keymaster_device_test.o \
+ trusty_keymaster_device.o \
+ $(KM)/aead_mode_operation.o \
+ $(KM)/aes_key.o \
+ $(KM)/aes_operation.o \
+ $(KM)/android_keymaster.o \
+ $(KM)/android_keymaster_messages.o \
+ $(KM)/android_keymaster_test_utils.o \
+ $(KM)/android_keymaster_utils.o \
+ $(KM)/asymmetric_key.o \
+ $(KM)/auth_encrypted_key_blob.o \
+ $(KM)/auth_encrypted_key_blob.o \
+ $(KM)/authorization_set.o \
+ $(KM)/ec_key.o \
+ $(KM)/ec_keymaster0_key.cpp \
+ $(KM)/ecdsa_operation.o \
+ $(KM)/hmac_key.o \
+ $(KM)/hmac_operation.o \
+ $(KM)/integrity_assured_key_blob.o \
+ $(KM)/key.o \
+ $(KM)/keymaster0_engine.o \
+ $(KM)/logger.o \
+ $(KM)/ocb.o \
+ $(KM)/ocb_utils.o \
+ $(KM)/openssl_err.o \
+ $(KM)/openssl_utils.o \
+ $(KM)/operation.o \
+ $(KM)/operation_table.o \
+ $(KM)/rsa_key.o \
+ $(KM)/rsa_keymaster0_key.o \
+ $(KM)/rsa_operation.o \
+ $(KM)/serializable.o \
+ $(KM)/soft_keymaster_context.o \
+ $(KM)/symmetric_key.o \
+ $(GTEST)/src/gtest-all.o
+
+$(GTEST)/src/gtest-all.o: CXXFLAGS:=$(subst -Wmissing-declarations,,$(CXXFLAGS))
+ocb.o: CFLAGS=$(CLANG_TEST_DEFINE)
+
+clean:
+ rm -f $(OBJS) $(DEPS) $(GCDA) $(GCNO) $(BINARIES) \
+ $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
+ coverage.info
+ rm -rf coverage
+
+-include $(CPPSRCS:.cpp=.d)
+-include $(CCSRCS:.cc=.d)
+
diff --git a/trusty/keymaster/legacy/module.cpp b/trusty/keymaster/legacy/module.cpp
new file mode 100644
index 000000000..7aa1a4eaa
--- /dev/null
+++ b/trusty/keymaster/legacy/module.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+using keymaster::TrustyKeymasterDevice;
+
+/*
+ * Generic device handling
+ */
+static int trusty_keymaster_open(const hw_module_t* module, const char* name,
+ hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
+ return -EINVAL;
+ }
+
+ TrustyKeymasterDevice* dev = new TrustyKeymasterDevice(module);
+ if (dev == NULL) {
+ return -ENOMEM;
+ }
+ *device = dev->hw_device();
+ // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
+ // exist until then.
+ return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+ .open = trusty_keymaster_open,
+};
+
+struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Trusty Keymaster HAL",
+ .author = "The Android Open Source Project",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
+ },
+};
diff --git a/trusty/keymaster/legacy/trusty_keymaster_device.cpp b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
new file mode 100644
index 000000000..88c3e7bbd
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
@@ -0,0 +1,761 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrustyKeymaster"
+
+#include <assert.h>
+#include <errno.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include <hardware/keymaster2.h>
+#include <keymaster/authorization_set.h>
+#include <log/log.h>
+
+#include <trusty_keymaster/ipc/keymaster_ipc.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 2048;
+
+namespace keymaster {
+
+TrustyKeymasterDevice::TrustyKeymasterDevice(const hw_module_t* module) {
+ static_assert(std::is_standard_layout<TrustyKeymasterDevice>::value,
+ "TrustyKeymasterDevice must be standard layout");
+ static_assert(offsetof(TrustyKeymasterDevice, device_) == 0,
+ "device_ must be the first member of TrustyKeymasterDevice");
+ static_assert(offsetof(TrustyKeymasterDevice, device_.common) == 0,
+ "common must be the first member of keymaster2_device");
+
+ ALOGI("Creating device");
+ ALOGD("Device address: %p", this);
+
+ device_ = {};
+
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t*>(module);
+ device_.common.close = close_device;
+
+ device_.flags = KEYMASTER_SUPPORTS_EC;
+
+ device_.configure = configure;
+ device_.add_rng_entropy = add_rng_entropy;
+ device_.generate_key = generate_key;
+ device_.get_key_characteristics = get_key_characteristics;
+ device_.import_key = import_key;
+ device_.export_key = export_key;
+ device_.attest_key = attest_key;
+ device_.upgrade_key = upgrade_key;
+ device_.delete_key = delete_key;
+ device_.delete_all_keys = delete_all_keys;
+ device_.begin = begin;
+ device_.update = update;
+ device_.finish = finish;
+ device_.abort = abort;
+
+ int rc = trusty_keymaster_connect();
+ error_ = translate_error(rc);
+ if (rc < 0) {
+ ALOGE("failed to connect to keymaster (%d)", rc);
+ return;
+ }
+
+ GetVersionRequest version_request;
+ GetVersionResponse version_response;
+ error_ = trusty_keymaster_send(KM_GET_VERSION, version_request, &version_response);
+ if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
+ ALOGE("\"Bad parameters\" error on GetVersion call. Version 0 is not supported.");
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ return;
+ }
+ message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
+ version_response.subminor_ver);
+ if (message_version_ < 0) {
+ // Can't translate version? Keymaster implementation must be newer.
+ ALOGE("Keymaster version %d.%d.%d not supported.", version_response.major_ver,
+ version_response.minor_ver, version_response.subminor_ver);
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ }
+}
+
+TrustyKeymasterDevice::~TrustyKeymasterDevice() {
+ trusty_keymaster_disconnect();
+}
+
+namespace {
+
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+ if (tmp) {
+ memcpy(tmp, buffer, size);
+ }
+ return tmp;
+}
+
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ RequestType* request) {
+ request->additional_params.Clear();
+ if (client_id && client_id->data_length > 0) {
+ request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+ }
+ if (app_data && app_data->data_length > 0) {
+ request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+ }
+}
+
+} // unnamed namespace
+
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+ ALOGD("Device received configure\n");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+
+ AuthorizationSet params_copy(*params);
+ ConfigureRequest request(message_version_);
+ if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
+ !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
+ ALOGD("Configuration parameters must contain OS version and patch level");
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+
+ ConfigureResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_CONFIGURE, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+ ALOGD("Device received add_rng_entropy");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ AddEntropyRequest request(message_version_);
+ request.random_data.Reinitialize(data, data_length);
+ AddEntropyResponse response(message_version_);
+ return trusty_keymaster_send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+ const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received generate_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!key_blob) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GenerateKeyRequest request(message_version_);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+ GenerateKeyResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_GENERATE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+ const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received get_key_characteristics");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_blob || !key_blob->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!characteristics) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GetKeyCharacteristicsRequest request(message_version_);
+ request.SetKeyMaterial(*key_blob);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ GetKeyCharacteristicsResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::import_key(
+ const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received import_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params || !key_data) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!key_blob) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ ImportKeyRequest request(message_version_);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+ request.key_format = key_format;
+ request.SetKeyMaterial(key_data->data, key_data->data_length);
+
+ ImportKeyResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_IMPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ ALOGD("Device received export_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_export || !key_to_export->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!export_data) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ export_data->data = nullptr;
+ export_data->data_length = 0;
+
+ ExportKeyRequest request(message_version_);
+ request.key_format = export_format;
+ request.SetKeyMaterial(*key_to_export);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ ExportKeyResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_EXPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ export_data->data_length = response.key_data_length;
+ export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+ if (!export_data->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ ALOGD("Device received attest_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_attest || !attest_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!cert_chain) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ cert_chain->entry_count = 0;
+ cert_chain->entries = nullptr;
+
+ AttestKeyRequest request(message_version_);
+ request.SetKeyMaterial(*key_to_attest);
+ request.attest_params.Reinitialize(*attest_params);
+
+ keymaster_blob_t attestation_challenge = {};
+ request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+ if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+ ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+ attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ AttestKeyResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_ATTEST_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ // Allocate and clear storage for cert_chain.
+ keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+ cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+ malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+ if (!cert_chain->entries) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entry_count = rsp_chain.entry_count;
+ for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+ entry = {};
+ }
+
+ // Copy cert_chain contents
+ size_t i = 0;
+ for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+ cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+ if (!cert_chain->entries[i].data) {
+ keymaster_free_cert_chain(cert_chain);
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entries[i].data_length = entry.data_length;
+ ++i;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(
+ const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ ALOGD("Device received upgrade_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_upgrade || !upgrade_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!upgraded_key) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ UpgradeKeyRequest request(message_version_);
+ request.SetKeyMaterial(*key_to_upgrade);
+ request.upgrade_params.Reinitialize(*upgrade_params);
+
+ UpgradeKeyResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_UPGRADE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+ upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+ response.upgraded_key.key_material_size);
+ if (!upgraded_key->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::begin(keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ ALOGD("Device received begin");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key || !key->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!operation_handle) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+
+ BeginOperationRequest request(message_version_);
+ request.purpose = purpose;
+ request.SetKeyMaterial(*key);
+ request.additional_params.Reinitialize(*in_params);
+
+ BeginOperationResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_BEGIN_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *operation_handle = response.op_handle;
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received update");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!input) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!input_consumed) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ UpdateOperationRequest request(message_version_);
+ request.op_handle = operation_handle;
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+ if (input && input->data_length > 0) {
+ size_t max_input_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - request.SerializedSize();
+ request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+ }
+
+ UpdateOperationResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_UPDATE_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *input_consumed = response.input_consumed;
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received finish");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (input && input->data_length > kMaximumFinishInputLength) {
+ ALOGE("%zu-byte input to finish; only %zu bytes allowed", input->data_length,
+ kMaximumFinishInputLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ FinishOperationRequest request(message_version_);
+ request.op_handle = operation_handle;
+ if (signature && signature->data && signature->data_length > 0) {
+ request.signature.Reinitialize(signature->data, signature->data_length);
+ }
+ if (input && input->data && input->data_length) {
+ request.input.Reinitialize(input->data, input->data_length);
+ }
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+
+ FinishOperationResponse response(message_version_);
+ keymaster_error_t err = trusty_keymaster_send(KM_FINISH_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t TrustyKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+ ALOGD("Device received abort");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ AbortOperationRequest request(message_version_);
+ request.op_handle = operation_handle;
+ AbortOperationResponse response(message_version_);
+ return trusty_keymaster_send(KM_ABORT_OPERATION, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster_key_blob_t* key) {
+ ALOGD("Device received delete_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ if (!key || !key->key_material)
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+ DeleteKeyRequest request(message_version_);
+ request.SetKeyMaterial(*key);
+ DeleteKeyResponse response(message_version_);
+ return trusty_keymaster_send(KM_DELETE_KEY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys() {
+ ALOGD("Device received delete_all_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ DeleteAllKeysRequest request(message_version_);
+ DeleteAllKeysResponse response(message_version_);
+ return trusty_keymaster_send(KM_DELETE_ALL_KEYS, request, &response);
+}
+
+hw_device_t* TrustyKeymasterDevice::hw_device() {
+ return &device_.common;
+}
+
+static inline TrustyKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+ return reinterpret_cast<TrustyKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
+}
+
+/* static */
+int TrustyKeymasterDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<TrustyKeymasterDevice*>(dev);
+ return 0;
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params) {
+ return convert_device(dev)->configure(params);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+ const uint8_t* data, size_t data_length) {
+ return convert_device(dev)->add_rng_entropy(data, data_length);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::generate_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->generate_key(params, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::get_key_characteristics(
+ const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+ characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::import_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+ export_data);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::upgrade_key(
+ const keymaster2_device_t* dev, const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key) {
+ return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::begin(const keymaster2_device_t* dev,
+ keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::update(
+ const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+ size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+ return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+ out_params, output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+ output);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle) {
+ return convert_device(dev)->abort(operation_handle);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key) {
+ return convert_device(dev)->delete_key(key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys(const keymaster2_device_t* dev) {
+ return convert_device(dev)->delete_all_keys();
+}
+
+} // namespace keymaster
diff --git a/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp b/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
new file mode 100644
index 000000000..68def5872
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_device_test.cpp
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <algorithm>
+#include <fstream>
+#include <memory>
+
+#include <gtest/gtest.h>
+#include <openssl/engine.h>
+
+#include <hardware/keymaster0.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+#include <keymaster/soft_keymaster_context.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+#include "android_keymaster_test_utils.h"
+#include "openssl_utils.h"
+
+using std::ifstream;
+using std::istreambuf_iterator;
+using std::string;
+
+static keymaster::AndroidKeymaster* impl_ = nullptr;
+
+extern "C" {
+int __android_log_print();
+}
+
+int __android_log_print() {
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ return result;
+}
+
+int trusty_keymaster_connect() {
+ impl_ = new keymaster::AndroidKeymaster(new keymaster::SoftKeymasterContext(nullptr), 16);
+}
+
+void trusty_keymaster_disconnect() {
+ delete static_cast<keymaster::AndroidKeymaster*>(priv_);
+}
+
+template <typename Req, typename Rsp>
+static int fake_call(keymaster::AndroidKeymaster* device,
+ void (keymaster::AndroidKeymaster::*method)(const Req&, Rsp*), void* in_buf,
+ uint32_t in_size, void* out_buf, uint32_t* out_size) {
+ Req req;
+ const uint8_t* in = static_cast<uint8_t*>(in_buf);
+ req.Deserialize(&in, in + in_size);
+ Rsp rsp;
+ (device->*method)(req, &rsp);
+
+ *out_size = rsp.SerializedSize();
+ uint8_t* out = static_cast<uint8_t*>(out_buf);
+ rsp.Serialize(out, out + *out_size);
+ return 0;
+}
+
+int trusty_keymaster_call(uint32_t cmd, void* in_buf, uint32_t in_size, void* out_buf,
+ uint32_t* out_size) {
+ switch (cmd) {
+ case KM_GENERATE_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::GenerateKey, in_buf, in_size,
+ out_buf, out_size);
+ case KM_BEGIN_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::BeginOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_UPDATE_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::UpdateOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_FINISH_OPERATION:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::FinishOperation, in_buf, in_size,
+ out_buf, out_size);
+ case KM_IMPORT_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::ImportKey, in_buf, in_size,
+ out_buf, out_size);
+ case KM_EXPORT_KEY:
+ return fake_call(impl_, &keymaster::AndroidKeymaster::ExportKey, in_buf, in_size,
+ out_buf, out_size);
+ }
+ return -EINVAL;
+}
+
+namespace keymaster {
+namespace test {
+
+class TrustyKeymasterTest : public testing::Test {
+ protected:
+ TrustyKeymasterTest() : device(NULL) {}
+
+ keymaster_rsa_keygen_params_t build_rsa_params() {
+ keymaster_rsa_keygen_params_t rsa_params;
+ rsa_params.public_exponent = 65537;
+ rsa_params.modulus_size = 2048;
+ return rsa_params;
+ }
+
+ uint8_t* build_message(size_t length) {
+ uint8_t* msg = new uint8_t[length];
+ memset(msg, 'a', length);
+ return msg;
+ }
+
+ size_t dsa_message_len(const keymaster_dsa_keygen_params_t& params) {
+ switch (params.key_size) {
+ case 256:
+ case 1024:
+ return 48;
+ case 2048:
+ case 4096:
+ return 72;
+ default:
+ // Oops.
+ return 0;
+ }
+ }
+
+ TrustyKeymasterDevice device;
+};
+
+class Malloc_Delete {
+ public:
+ Malloc_Delete(void* p) : p_(p) {}
+ ~Malloc_Delete() { free(p_); }
+
+ private:
+ void* p_;
+};
+
+typedef TrustyKeymasterTest KeyGenTest;
+TEST_F(KeyGenTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+}
+
+TEST_F(KeyGenTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t ec_params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &ec_params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+}
+
+typedef TrustyKeymasterTest SigningTest;
+TEST_F(SigningTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(message_len, siglen);
+}
+
+TEST_F(SigningTest, RsaShortMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8 - 1;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
+ message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, RsaLongMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8 + 1;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
+ message_len, &signature, &siglen));
+}
+
+TEST_F(SigningTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaEmptyMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+TEST_F(SigningTest, EcdsaLargeMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ size_t message_len = 1024 * 7;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+ // contents of message don't matter.
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_GT(siglen, 69U);
+ EXPECT_LT(siglen, 73U);
+}
+
+typedef TrustyKeymasterTest VerificationTest;
+TEST_F(VerificationTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+ signature, siglen));
+}
+
+TEST_F(VerificationTest, RsaBadSignature) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+
+ Malloc_Delete sig_deleter(signature);
+ signature[siglen / 2]++;
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, RsaBadMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ message[0]++;
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, RsaShortMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len - 1, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, RsaLongMessage) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len + 1));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH,
+ device.verify_data(&sig_params, ptr, size, message.get(), message_len + 1, signature,
+ siglen));
+}
+
+TEST_F(VerificationTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message,
+ array_size(message) - 1, signature, siglen));
+}
+
+TEST_F(VerificationTest, EcdsaLargeMessageSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ size_t message_len = 1024 * 7;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+ // contents of message don't matter.
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, ptr, size, message.get(), message_len,
+ signature, siglen));
+}
+
+static string read_file(const string& file_name) {
+ ifstream file_stream(file_name, std::ios::binary);
+ istreambuf_iterator<char> file_begin(file_stream);
+ istreambuf_iterator<char> file_end;
+ return string(file_begin, file_end);
+}
+
+typedef TrustyKeymasterTest ImportKeyTest;
+TEST_F(ImportKeyTest, RsaSuccess) {
+ string pk8_key = read_file("../../../../system/keymaster/rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+ pk8_key.size(), &key, &size));
+ Malloc_Delete key_deleter(key);
+
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_size = 1024 /* key size */ / 8;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
+ memset(message.get(), 'a', message_size);
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message.get(), message_size,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message.get(), message_size,
+ signature, siglen));
+}
+
+TEST_F(ImportKeyTest, EcdsaSuccess) {
+ string pk8_key = read_file("../../../../system/keymaster/ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(KM_ERROR_OK, device.import_keypair(reinterpret_cast<const uint8_t*>(pk8_key.data()),
+ pk8_key.size(), &key, &size));
+ Malloc_Delete key_deleter(key);
+
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+ array_size(message) - 1, signature, siglen));
+}
+
+struct EVP_PKEY_CTX_Delete {
+ void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
+ size_t signature_len, const uint8_t* message, size_t message_len) {
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+ ASSERT_TRUE(pkey.get() != NULL);
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ ASSERT_TRUE(ctx.get() != NULL);
+ ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
+ if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
+ ASSERT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING));
+ EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), signature, signature_len, message, message_len));
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, RsaSuccess) {
+ keymaster_rsa_keygen_params_t params = build_rsa_params();
+ uint8_t* ptr = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_RSA, &params, &ptr, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(ptr);
+
+ uint8_t* exported;
+ size_t exported_size;
+ EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(ptr, size, &exported, &exported_size));
+ Malloc_Delete exported_deleter(exported);
+
+ // Sign a message so we can verify it with the exported pubkey.
+ keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
+ size_t message_len = params.modulus_size / 8;
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
+ uint8_t* signature;
+ size_t siglen;
+ EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
+ &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(message_len, siglen);
+ const uint8_t* tmp = exported;
+
+ VerifySignature(exported, exported_size, signature, siglen, message.get(), message_len);
+}
+
+typedef TrustyKeymasterTest ExportKeyTest;
+TEST_F(ExportKeyTest, EcdsaSuccess) {
+ keymaster_ec_keygen_params_t params = {256};
+ uint8_t* key = NULL;
+ size_t size;
+ ASSERT_EQ(0, device.generate_keypair(TYPE_EC, &params, &key, &size));
+ EXPECT_GT(size, 0U);
+ Malloc_Delete key_deleter(key);
+
+ uint8_t* exported;
+ size_t exported_size;
+ EXPECT_EQ(KM_ERROR_OK, device.get_keypair_public(key, size, &exported, &exported_size));
+ Malloc_Delete exported_deleter(exported);
+
+ // Sign a message so we can verify it with the exported pubkey.
+ keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
+ uint8_t message[] = "12345678901234567890123456789012";
+ uint8_t* signature;
+ size_t siglen;
+ ASSERT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, key, size, message,
+ array_size(message) - 1, &signature, &siglen));
+ Malloc_Delete sig_deleter(signature);
+ EXPECT_EQ(KM_ERROR_OK, device.verify_data(&sig_params, key, size, message,
+ array_size(message) - 1, signature, siglen));
+
+ VerifySignature(exported, exported_size, signature, siglen, message, array_size(message) - 1);
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/trusty/keymaster/legacy/trusty_keymaster_main.cpp b/trusty/keymaster/legacy/trusty_keymaster_main.cpp
new file mode 100644
index 000000000..e3e70e66e
--- /dev/null
+++ b/trusty/keymaster/legacy/trusty_keymaster_main.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keymaster/keymaster_configuration.h>
+
+#include <stdio.h>
+#include <memory>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <trusty_keymaster/legacy/trusty_keymaster_device.h>
+
+using keymaster::TrustyKeymasterDevice;
+
+unsigned char rsa_privkey_pk8_der[] = {
+ 0x30, 0x82, 0x02, 0x75, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5f, 0x30, 0x82, 0x02, 0x5b,
+ 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xc6, 0x09, 0x54, 0x09, 0x04, 0x7d, 0x86, 0x34,
+ 0x81, 0x2d, 0x5a, 0x21, 0x81, 0x76, 0xe4, 0x5c, 0x41, 0xd6, 0x0a, 0x75, 0xb1, 0x39, 0x01,
+ 0xf2, 0x34, 0x22, 0x6c, 0xff, 0xe7, 0x76, 0x52, 0x1c, 0x5a, 0x77, 0xb9, 0xe3, 0x89, 0x41,
+ 0x7b, 0x71, 0xc0, 0xb6, 0xa4, 0x4d, 0x13, 0xaf, 0xe4, 0xe4, 0xa2, 0x80, 0x5d, 0x46, 0xc9,
+ 0xda, 0x29, 0x35, 0xad, 0xb1, 0xff, 0x0c, 0x1f, 0x24, 0xea, 0x06, 0xe6, 0x2b, 0x20, 0xd7,
+ 0x76, 0x43, 0x0a, 0x4d, 0x43, 0x51, 0x57, 0x23, 0x3c, 0x6f, 0x91, 0x67, 0x83, 0xc3, 0x0e,
+ 0x31, 0x0f, 0xcb, 0xd8, 0x9b, 0x85, 0xc2, 0xd5, 0x67, 0x71, 0x16, 0x97, 0x85, 0xac, 0x12,
+ 0xbc, 0xa2, 0x44, 0xab, 0xda, 0x72, 0xbf, 0xb1, 0x9f, 0xc4, 0x4d, 0x27, 0xc8, 0x1e, 0x1d,
+ 0x92, 0xde, 0x28, 0x4f, 0x40, 0x61, 0xed, 0xfd, 0x99, 0x28, 0x07, 0x45, 0xea, 0x6d, 0x25,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x1b, 0xe0, 0xf0, 0x4d, 0x9c, 0xae, 0x37,
+ 0x18, 0x69, 0x1f, 0x03, 0x53, 0x38, 0x30, 0x8e, 0x91, 0x56, 0x4b, 0x55, 0x89, 0x9f, 0xfb,
+ 0x50, 0x84, 0xd2, 0x46, 0x0e, 0x66, 0x30, 0x25, 0x7e, 0x05, 0xb3, 0xce, 0xab, 0x02, 0x97,
+ 0x2d, 0xfa, 0xbc, 0xd6, 0xce, 0x5f, 0x6e, 0xe2, 0x58, 0x9e, 0xb6, 0x79, 0x11, 0xed, 0x0f,
+ 0xac, 0x16, 0xe4, 0x3a, 0x44, 0x4b, 0x8c, 0x86, 0x1e, 0x54, 0x4a, 0x05, 0x93, 0x36, 0x57,
+ 0x72, 0xf8, 0xba, 0xf6, 0xb2, 0x2f, 0xc9, 0xe3, 0xc5, 0xf1, 0x02, 0x4b, 0x06, 0x3a, 0xc0,
+ 0x80, 0xa7, 0xb2, 0x23, 0x4c, 0xf8, 0xae, 0xe8, 0xf6, 0xc4, 0x7b, 0xbf, 0x4f, 0xd3, 0xac,
+ 0xe7, 0x24, 0x02, 0x90, 0xbe, 0xf1, 0x6c, 0x0b, 0x3f, 0x7f, 0x3c, 0xdd, 0x64, 0xce, 0x3a,
+ 0xb5, 0x91, 0x2c, 0xf6, 0xe3, 0x2f, 0x39, 0xab, 0x18, 0x83, 0x58, 0xaf, 0xcc, 0xcd, 0x80,
+ 0x81, 0x02, 0x41, 0x00, 0xe4, 0xb4, 0x9e, 0xf5, 0x0f, 0x76, 0x5d, 0x3b, 0x24, 0xdd, 0xe0,
+ 0x1a, 0xce, 0xaa, 0xf1, 0x30, 0xf2, 0xc7, 0x66, 0x70, 0xa9, 0x1a, 0x61, 0xae, 0x08, 0xaf,
+ 0x49, 0x7b, 0x4a, 0x82, 0xbe, 0x6d, 0xee, 0x8f, 0xcd, 0xd5, 0xe3, 0xf7, 0xba, 0x1c, 0xfb,
+ 0x1f, 0x0c, 0x92, 0x6b, 0x88, 0xf8, 0x8c, 0x92, 0xbf, 0xab, 0x13, 0x7f, 0xba, 0x22, 0x85,
+ 0x22, 0x7b, 0x83, 0xc3, 0x42, 0xff, 0x7c, 0x55, 0x02, 0x41, 0x00, 0xdd, 0xab, 0xb5, 0x83,
+ 0x9c, 0x4c, 0x7f, 0x6b, 0xf3, 0xd4, 0x18, 0x32, 0x31, 0xf0, 0x05, 0xb3, 0x1a, 0xa5, 0x8a,
+ 0xff, 0xdd, 0xa5, 0xc7, 0x9e, 0x4c, 0xce, 0x21, 0x7f, 0x6b, 0xc9, 0x30, 0xdb, 0xe5, 0x63,
+ 0xd4, 0x80, 0x70, 0x6c, 0x24, 0xe9, 0xeb, 0xfc, 0xab, 0x28, 0xa6, 0xcd, 0xef, 0xd3, 0x24,
+ 0xb7, 0x7e, 0x1b, 0xf7, 0x25, 0x1b, 0x70, 0x90, 0x92, 0xc2, 0x4f, 0xf5, 0x01, 0xfd, 0x91,
+ 0x02, 0x40, 0x23, 0xd4, 0x34, 0x0e, 0xda, 0x34, 0x45, 0xd8, 0xcd, 0x26, 0xc1, 0x44, 0x11,
+ 0xda, 0x6f, 0xdc, 0xa6, 0x3c, 0x1c, 0xcd, 0x4b, 0x80, 0xa9, 0x8a, 0xd5, 0x2b, 0x78, 0xcc,
+ 0x8a, 0xd8, 0xbe, 0xb2, 0x84, 0x2c, 0x1d, 0x28, 0x04, 0x05, 0xbc, 0x2f, 0x6c, 0x1b, 0xea,
+ 0x21, 0x4a, 0x1d, 0x74, 0x2a, 0xb9, 0x96, 0xb3, 0x5b, 0x63, 0xa8, 0x2a, 0x5e, 0x47, 0x0f,
+ 0xa8, 0x8d, 0xbf, 0x82, 0x3c, 0xdd, 0x02, 0x40, 0x1b, 0x7b, 0x57, 0x44, 0x9a, 0xd3, 0x0d,
+ 0x15, 0x18, 0x24, 0x9a, 0x5f, 0x56, 0xbb, 0x98, 0x29, 0x4d, 0x4b, 0x6a, 0xc1, 0x2f, 0xfc,
+ 0x86, 0x94, 0x04, 0x97, 0xa5, 0xa5, 0x83, 0x7a, 0x6c, 0xf9, 0x46, 0x26, 0x2b, 0x49, 0x45,
+ 0x26, 0xd3, 0x28, 0xc1, 0x1e, 0x11, 0x26, 0x38, 0x0f, 0xde, 0x04, 0xc2, 0x4f, 0x91, 0x6d,
+ 0xec, 0x25, 0x08, 0x92, 0xdb, 0x09, 0xa6, 0xd7, 0x7c, 0xdb, 0xa3, 0x51, 0x02, 0x40, 0x77,
+ 0x62, 0xcd, 0x8f, 0x4d, 0x05, 0x0d, 0xa5, 0x6b, 0xd5, 0x91, 0xad, 0xb5, 0x15, 0xd2, 0x4d,
+ 0x7c, 0xcd, 0x32, 0xcc, 0xa0, 0xd0, 0x5f, 0x86, 0x6d, 0x58, 0x35, 0x14, 0xbd, 0x73, 0x24,
+ 0xd5, 0xf3, 0x36, 0x45, 0xe8, 0xed, 0x8b, 0x4a, 0x1c, 0xb3, 0xcc, 0x4a, 0x1d, 0x67, 0x98,
+ 0x73, 0x99, 0xf2, 0xa0, 0x9f, 0x5b, 0x3f, 0xb6, 0x8c, 0x88, 0xd5, 0xe5, 0xd9, 0x0a, 0xc3,
+ 0x34, 0x92, 0xd6};
+unsigned int rsa_privkey_pk8_der_len = 633;
+
+unsigned char dsa_privkey_pk8_der[] = {
+ 0x30, 0x82, 0x01, 0x4b, 0x02, 0x01, 0x00, 0x30, 0x82, 0x01, 0x2b, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x38, 0x04, 0x01, 0x30, 0x82, 0x01, 0x1e, 0x02, 0x81, 0x81, 0x00, 0xa3, 0xf3,
+ 0xe9, 0xb6, 0x7e, 0x7d, 0x88, 0xf6, 0xb7, 0xe5, 0xf5, 0x1f, 0x3b, 0xee, 0xac, 0xd7, 0xad,
+ 0xbc, 0xc9, 0xd1, 0x5a, 0xf8, 0x88, 0xc4, 0xef, 0x6e, 0x3d, 0x74, 0x19, 0x74, 0xe7, 0xd8,
+ 0xe0, 0x26, 0x44, 0x19, 0x86, 0xaf, 0x19, 0xdb, 0x05, 0xe9, 0x3b, 0x8b, 0x58, 0x58, 0xde,
+ 0xe5, 0x4f, 0x48, 0x15, 0x01, 0xea, 0xe6, 0x83, 0x52, 0xd7, 0xc1, 0x21, 0xdf, 0xb9, 0xb8,
+ 0x07, 0x66, 0x50, 0xfb, 0x3a, 0x0c, 0xb3, 0x85, 0xee, 0xbb, 0x04, 0x5f, 0xc2, 0x6d, 0x6d,
+ 0x95, 0xfa, 0x11, 0x93, 0x1e, 0x59, 0x5b, 0xb1, 0x45, 0x8d, 0xe0, 0x3d, 0x73, 0xaa, 0xf2,
+ 0x41, 0x14, 0x51, 0x07, 0x72, 0x3d, 0xa2, 0xf7, 0x58, 0xcd, 0x11, 0xa1, 0x32, 0xcf, 0xda,
+ 0x42, 0xb7, 0xcc, 0x32, 0x80, 0xdb, 0x87, 0x82, 0xec, 0x42, 0xdb, 0x5a, 0x55, 0x24, 0x24,
+ 0xa2, 0xd1, 0x55, 0x29, 0xad, 0xeb, 0x02, 0x15, 0x00, 0xeb, 0xea, 0x17, 0xd2, 0x09, 0xb3,
+ 0xd7, 0x21, 0x9a, 0x21, 0x07, 0x82, 0x8f, 0xab, 0xfe, 0x88, 0x71, 0x68, 0xf7, 0xe3, 0x02,
+ 0x81, 0x80, 0x19, 0x1c, 0x71, 0xfd, 0xe0, 0x03, 0x0c, 0x43, 0xd9, 0x0b, 0xf6, 0xcd, 0xd6,
+ 0xa9, 0x70, 0xe7, 0x37, 0x86, 0x3a, 0x78, 0xe9, 0xa7, 0x47, 0xa7, 0x47, 0x06, 0x88, 0xb1,
+ 0xaf, 0xd7, 0xf3, 0xf1, 0xa1, 0xd7, 0x00, 0x61, 0x28, 0x88, 0x31, 0x48, 0x60, 0xd8, 0x11,
+ 0xef, 0xa5, 0x24, 0x1a, 0x81, 0xc4, 0x2a, 0xe2, 0xea, 0x0e, 0x36, 0xd2, 0xd2, 0x05, 0x84,
+ 0x37, 0xcf, 0x32, 0x7d, 0x09, 0xe6, 0x0f, 0x8b, 0x0c, 0xc8, 0xc2, 0xa4, 0xb1, 0xdc, 0x80,
+ 0xca, 0x68, 0xdf, 0xaf, 0xd2, 0x90, 0xc0, 0x37, 0x58, 0x54, 0x36, 0x8f, 0x49, 0xb8, 0x62,
+ 0x75, 0x8b, 0x48, 0x47, 0xc0, 0xbe, 0xf7, 0x9a, 0x92, 0xa6, 0x68, 0x05, 0xda, 0x9d, 0xaf,
+ 0x72, 0x9a, 0x67, 0xb3, 0xb4, 0x14, 0x03, 0xae, 0x4f, 0x4c, 0x76, 0xb9, 0xd8, 0x64, 0x0a,
+ 0xba, 0x3b, 0xa8, 0x00, 0x60, 0x4d, 0xae, 0x81, 0xc3, 0xc5, 0x04, 0x17, 0x02, 0x15, 0x00,
+ 0x81, 0x9d, 0xfd, 0x53, 0x0c, 0xc1, 0x8f, 0xbe, 0x8b, 0xea, 0x00, 0x26, 0x19, 0x29, 0x33,
+ 0x91, 0x84, 0xbe, 0xad, 0x81};
+unsigned int dsa_privkey_pk8_der_len = 335;
+
+unsigned char ec_privkey_pk8_der[] = {
+ 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04,
+ 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x73, 0x7c, 0x2e, 0xcd, 0x7b, 0x8d,
+ 0x19, 0x40, 0xbf, 0x29, 0x30, 0xaa, 0x9b, 0x4e, 0xd3, 0xff, 0x94, 0x1e, 0xed, 0x09,
+ 0x36, 0x6b, 0xc0, 0x32, 0x99, 0x98, 0x64, 0x81, 0xf3, 0xa4, 0xd8, 0x59, 0xa1, 0x44,
+ 0x03, 0x42, 0x00, 0x04, 0xbf, 0x85, 0xd7, 0x72, 0x0d, 0x07, 0xc2, 0x54, 0x61, 0x68,
+ 0x3b, 0xc6, 0x48, 0xb4, 0x77, 0x8a, 0x9a, 0x14, 0xdd, 0x8a, 0x02, 0x4e, 0x3b, 0xdd,
+ 0x8c, 0x7d, 0xdd, 0x9a, 0xb2, 0xb5, 0x28, 0xbb, 0xc7, 0xaa, 0x1b, 0x51, 0xf1, 0x4e,
+ 0xbb, 0xbb, 0x0b, 0xd0, 0xce, 0x21, 0xbc, 0xc4, 0x1c, 0x6e, 0xb0, 0x00, 0x83, 0xcf,
+ 0x33, 0x76, 0xd1, 0x1f, 0xd4, 0x49, 0x49, 0xe0, 0xb2, 0x18, 0x3b, 0xfe};
+unsigned int ec_privkey_pk8_der_len = 138;
+
+keymaster_key_param_t ec_params[] = {
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC),
+ keymaster_param_long(KM_TAG_EC_CURVE, KM_EC_CURVE_P_521),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t ec_param_set = {ec_params, sizeof(ec_params) / sizeof(*ec_params)};
+
+keymaster_key_param_t rsa_params[] = {
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ keymaster_param_int(KM_TAG_KEY_SIZE, 1024),
+ keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, 65537),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+};
+keymaster_key_param_set_t rsa_param_set = {rsa_params, sizeof(rsa_params) / sizeof(*rsa_params)};
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+
+struct EVP_PKEY_CTX_Delete {
+ void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); }
+};
+
+static bool do_operation(TrustyKeymasterDevice* device, keymaster_purpose_t purpose,
+ keymaster_key_blob_t* key, keymaster_blob_t* input,
+ keymaster_blob_t* signature, keymaster_blob_t* output) {
+ keymaster_key_param_t params[] = {
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ };
+ keymaster_key_param_set_t param_set = {params, sizeof(params) / sizeof(*params)};
+ keymaster_operation_handle_t op_handle;
+ keymaster_error_t error = device->begin(purpose, key, &param_set, nullptr, &op_handle);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster begin() failed: %d\n", error);
+ return false;
+ }
+ size_t input_consumed;
+ error = device->update(op_handle, nullptr, input, &input_consumed, nullptr, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster update() failed: %d\n", error);
+ return false;
+ }
+ if (input_consumed != input->data_length) {
+ // This should never happen. If it does, it's a bug in the keymaster implementation.
+ printf("Keymaster update() did not consume all data.\n");
+ device->abort(op_handle);
+ return false;
+ }
+ error = device->finish(op_handle, nullptr, nullptr, signature, nullptr, output);
+ if (error != KM_ERROR_OK) {
+ printf("Keymaster finish() failed: %d\n", error);
+ return false;
+ }
+ return true;
+}
+
+static bool test_import_rsa(TrustyKeymasterDevice* device) {
+ printf("===================\n");
+ printf("= RSA Import Test =\n");
+ printf("===================\n\n");
+
+ printf("=== Importing RSA keypair === \n");
+ keymaster_key_blob_t key;
+ keymaster_blob_t private_key = {rsa_privkey_pk8_der, rsa_privkey_pk8_der_len};
+ int error =
+ device->import_key(&rsa_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Error importing RSA key: %d\n\n", error);
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+ printf("=== Signing with imported RSA key ===\n");
+ size_t message_len = 1024 / 8;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with imported RSA key\n\n");
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+ printf("=== Verifying with imported RSA key === \n");
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with imported RSA key\n\n");
+ return false;
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_rsa(TrustyKeymasterDevice* device) {
+ printf("============\n");
+ printf("= RSA Test =\n");
+ printf("============\n\n");
+
+ printf("=== Generating RSA key pair ===\n");
+ keymaster_key_blob_t key;
+ int error = device->generate_key(&rsa_param_set, &key, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Error generating RSA key pair: %d\n\n", error);
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+ printf("=== Signing with RSA key === \n");
+ size_t message_len = 1024 / 8;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with RSA key\n\n");
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+ printf("=== Verifying with RSA key === \n");
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with RSA key\n\n");
+ return false;
+ }
+
+ printf("=== Exporting RSA public key ===\n");
+ keymaster_blob_t exported_key;
+ error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
+ if (error != KM_ERROR_OK) {
+ printf("Error exporting RSA public key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Verifying with exported key ===\n");
+ const uint8_t* tmp = exported_key.data;
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+ printf("Error initializing openss EVP context\n\n");
+ return false;
+ }
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ printf("Exported key was the wrong type?!?\n\n");
+ return false;
+ }
+
+ EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+ if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+ message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n\n");
+ return false;
+ } else {
+ printf("Verification succeeded\n");
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_import_ecdsa(TrustyKeymasterDevice* device) {
+ printf("=====================\n");
+ printf("= ECDSA Import Test =\n");
+ printf("=====================\n\n");
+
+ printf("=== Importing ECDSA keypair === \n");
+ keymaster_key_blob_t key;
+ keymaster_blob_t private_key = {ec_privkey_pk8_der, ec_privkey_pk8_der_len};
+ int error = device->import_key(&ec_param_set, KM_KEY_FORMAT_PKCS8, &private_key, &key, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Error importing ECDSA key: %d\n\n", error);
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> deleter(key.key_material);
+
+ printf("=== Signing with imported ECDSA key ===\n");
+ size_t message_len = 30 /* arbitrary */;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with imported ECDSA key\n\n");
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+ printf("=== Verifying with imported ECDSA key === \n");
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with imported ECDSA key\n\n");
+ return false;
+ }
+
+ printf("\n");
+ return true;
+}
+
+static bool test_ecdsa(TrustyKeymasterDevice* device) {
+ printf("==============\n");
+ printf("= ECDSA Test =\n");
+ printf("==============\n\n");
+
+ printf("=== Generating ECDSA key pair ===\n");
+ keymaster_key_blob_t key;
+ int error = device->generate_key(&ec_param_set, &key, nullptr);
+ if (error != KM_ERROR_OK) {
+ printf("Error generating ECDSA key pair: %d\n\n", error);
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> key_deleter(key.key_material);
+
+ printf("=== Signing with ECDSA key === \n");
+ size_t message_len = 30 /* arbitrary */;
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
+ memset(message.get(), 'a', message_len);
+ keymaster_blob_t input = {message.get(), message_len}, signature;
+
+ if (!do_operation(device, KM_PURPOSE_SIGN, &key, &input, nullptr, &signature)) {
+ printf("Error signing data with ECDSA key\n\n");
+ return false;
+ }
+ std::unique_ptr<const uint8_t[]> signature_deleter(signature.data);
+
+ printf("=== Verifying with ECDSA key === \n");
+ if (!do_operation(device, KM_PURPOSE_VERIFY, &key, &input, &signature, nullptr)) {
+ printf("Error verifying data with ECDSA key\n\n");
+ return false;
+ }
+
+ printf("=== Exporting ECDSA public key ===\n");
+ keymaster_blob_t exported_key;
+ error = device->export_key(KM_KEY_FORMAT_X509, &key, nullptr, nullptr, &exported_key);
+ if (error != KM_ERROR_OK) {
+ printf("Error exporting ECDSA public key: %d\n\n", error);
+ return false;
+ }
+
+ printf("=== Verifying with exported key ===\n");
+ const uint8_t* tmp = exported_key.data;
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(NULL, &tmp, exported_key.data_length));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ if (EVP_PKEY_verify_init(ctx.get()) != 1) {
+ printf("Error initializing openssl EVP context\n\n");
+ return false;
+ }
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
+ printf("Exported key was the wrong type?!?\n\n");
+ return false;
+ }
+
+ if (EVP_PKEY_verify(ctx.get(), signature.data, signature.data_length, message.get(),
+ message_len) != 1) {
+ printf("Verification with exported pubkey failed.\n\n");
+ return false;
+ } else {
+ printf("Verification succeeded\n");
+ }
+
+ printf("\n");
+ return true;
+}
+
+int main(void) {
+ TrustyKeymasterDevice device(NULL);
+ keymaster::ConfigureDevice(reinterpret_cast<keymaster2_device_t*>(&device));
+ if (device.session_error() != KM_ERROR_OK) {
+ printf("Failed to initialize Trusty session: %d\n", device.session_error());
+ return 1;
+ }
+ printf("Trusty session initialized\n");
+
+ bool success = true;
+ success &= test_rsa(&device);
+ success &= test_import_rsa(&device);
+ success &= test_ecdsa(&device);
+ success &= test_import_ecdsa(&device);
+
+ if (success) {
+ printf("\nTESTS PASSED!\n");
+ } else {
+ printf("\n!!!!TESTS FAILED!!!\n");
+ }
+
+ return success ? 0 : 1;
+}
diff --git a/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml b/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml
deleted file mode 100644
index fce2ac219..000000000
--- a/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-<?xml version="1.0"?>
-<AndroidAttestation>
- <NumberOfKeyboxes>10</NumberOfKeyboxes>
- <Keybox DeviceID="dev1">
- <Key algorithm="rsa">
- <PrivateKey format="pem">
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb
-KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI
-O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB
-AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3
-wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C
-woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs
-NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU
-0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy
-DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY
-ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3
-TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y
-qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x
-fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l
------END RSA PRIVATE KEY-----
- </PrivateKey>
- <CertificateChain>
- <NumberOfCertificates>2</NumberOfCertificates>
- <Certificate format="pem">
------BEGIN CERTIFICATE-----
-MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx
-EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
-BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx
-MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
-YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv
-aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf
-MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1
-qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX
-4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd
-XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ
-k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw
-BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn
-M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4
-Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f
-9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8=
------END CERTIFICATE-----
- </Certificate>
- <Certificate format="pem">
------BEGIN CERTIFICATE-----
-MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
-BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
-aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN
-MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG
-A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE
-CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs
-Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL
-t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID
-AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw
-FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
-Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j
-HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc
-DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op
-Lvtc9mL1J1IXvmM=
------END CERTIFICATE-----
- </Certificate>
- </CertificateChain>
- </Key>
- </Keybox>
- <Keybox DeviceID="dev1">
- <Key algorithm="ecdsa">
- <PrivateKey format="pem">
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49
-AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l
-uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA==
------END EC PRIVATE KEY-----
- </PrivateKey>
- <CertificateChain>
- <NumberOfCertificates>2</NumberOfCertificates>
- <Certificate format="pem">
------BEGIN CERTIFICATE-----
-MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
-EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
-VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
-ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
-MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
-CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
-bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
-dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
-efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
-U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
-qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
-AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
-wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
-Xvsiu+f+uXc/WT/7
------END CERTIFICATE-----
- </Certificate>
- <Certificate format="pem">
------BEGIN CERTIFICATE-----
-MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
-EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
-dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
-VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
-HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
-EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
-BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
-QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
-KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
-dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
-BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
-EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
-SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
-C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
------END CERTIFICATE-----
- </Certificate>
- </CertificateChain>
- </Key>
- </Keybox>
-</AndroidAttestation>
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
deleted file mode 100644
index df6b0f833..000000000
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <libxml/xmlreader.h>
-#include <openssl/pem.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <string>
-
-using std::string;
-
-#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
-
-static const char* _sopts = "h";
-static const struct option _lopts[] = {
- {"help", no_argument, 0, 'h'},
- {0, 0, 0, 0},
-};
-
-static const char* usage =
- "Usage: %s [options] xml-file\n"
- "\n"
- "options:\n"
- " -h, --help prints this message and exit\n"
- "\n";
-
-static void print_usage_and_exit(const char* prog, int code) {
- fprintf(stderr, usage, prog);
- exit(code);
-}
-
-static void parse_options(int argc, char** argv) {
- int c;
- int oidx = 0;
-
- while (1) {
- c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
- if (c == -1) {
- break; /* done */
- }
-
- switch (c) {
- case 'h':
- print_usage_and_exit(argv[0], EXIT_SUCCESS);
- break;
-
- default:
- print_usage_and_exit(argv[0], EXIT_FAILURE);
- }
- }
-}
-
-struct SetAttestationKeyRequest : public keymaster::KeymasterMessage {
- explicit SetAttestationKeyRequest(int32_t ver = keymaster::kDefaultMessageVersion)
- : KeymasterMessage(ver) {}
-
- size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }
- uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
- buf = keymaster::append_uint32_to_buf(buf, end, algorithm);
- return key_data.Serialize(buf, end);
- }
- bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
- return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm) &&
- key_data.Deserialize(buf_ptr, end);
- }
-
- keymaster_algorithm_t algorithm;
- keymaster::Buffer key_data;
-};
-
-struct KeymasterNoResponse : public keymaster::KeymasterResponse {
- explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)
- : keymaster::KeymasterResponse(ver) {}
-
- size_t NonErrorSerializedSize() const override { return 0; }
- uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }
- bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }
-};
-
-struct SetAttestationKeyResponse : public KeymasterNoResponse {};
-
-struct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {
- explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::kDefaultMessageVersion)
- : KeymasterMessage(ver) {}
-
- size_t SerializedSize() const override { return sizeof(uint32_t); }
- uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
- return keymaster::append_uint32_to_buf(buf, end, algorithm);
- }
- bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
- return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm);
- }
-
- keymaster_algorithm_t algorithm;
-};
-
-struct ClearAttestationCertChainResponse : public KeymasterNoResponse {};
-
-static int set_attestation_key_or_cert_bin(uint32_t cmd, keymaster_algorithm_t algorithm,
- const void* key_data, size_t key_data_size) {
- int ret;
-
- SetAttestationKeyRequest req;
- req.algorithm = algorithm;
- req.key_data.Reinitialize(key_data, key_data_size);
- SetAttestationKeyResponse rsp;
-
- ret = trusty_keymaster_send(cmd, req, &rsp);
- if (ret) {
- fprintf(stderr, "trusty_keymaster_send cmd 0x%x failed %d\n", cmd, ret);
- return ret;
- }
-
- return 0;
-}
-
-static int set_attestation_key_or_cert_pem(uint32_t cmd, keymaster_algorithm_t algorithm,
- const xmlChar* pemkey) {
- int ret;
- int sslret;
-
- /* Convert from pem to binary */
- BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));
- if (!bio) {
- fprintf(stderr, "BIO_new_mem_buf failed\n");
- ERR_print_errors_fp(stderr);
- return -1;
- }
-
- char* key_name;
- char* key_header;
- uint8_t* key;
- long keylen;
- sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);
- BIO_free(bio);
-
- if (!sslret) {
- fprintf(stderr, "PEM_read_bio failed\n");
- ERR_print_errors_fp(stderr);
- return -1;
- }
-
- /* Send key in binary format to trusty */
- ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);
-
- OPENSSL_free(key_name);
- OPENSSL_free(key_header);
- OPENSSL_free(key);
-
- return ret;
-}
-
-static int set_attestation_key_or_cert_iecs(uint32_t cmd, keymaster_algorithm_t algorithm,
- const xmlChar* key_base64) {
- int ret;
- int sslret;
-
- /* Remove all whitespace. EVP_DecodeBase64 does not support whitespace. */
- string key_base64_str((const char*)key_base64);
- key_base64_str.erase(remove_if(key_base64_str.begin(), key_base64_str.end(), isspace),
- key_base64_str.end());
-
- /* Convert from base64 to binary */
- uint8_t* key;
- size_t keylen;
- size_t key_base64_len = key_base64_str.length();
-
- sslret = EVP_DecodedLength(&keylen, key_base64_len);
- if (!sslret) {
- fprintf(stderr, "invalid input length, %zu\n", key_base64_len);
- return -1;
- }
- key = (uint8_t*)malloc(keylen);
- if (!key) {
- fprintf(stderr, "failed to allocate key, size %zu\n", key_base64_len);
- return -1;
- }
- sslret = EVP_DecodeBase64(key, &keylen, keylen, (const uint8_t*)key_base64_str.data(),
- key_base64_len);
- if (!sslret) {
- fprintf(stderr, "EVP_DecodeBase64 failed\n");
- ERR_print_errors_fp(stderr);
- free(key);
- return -1;
- }
-
- /* Send key in binary format to trusty */
- ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);
-
- free(key);
-
- return ret;
-}
-
-static int str_to_algorithm(keymaster_algorithm_t* algorithm, const xmlChar* algorithm_str) {
- if (xmlStrEqual(algorithm_str, BAD_CAST "rsa")) {
- *algorithm = KM_ALGORITHM_RSA;
- } else if (xmlStrEqual(algorithm_str, BAD_CAST "ecdsa")) {
- *algorithm = KM_ALGORITHM_EC;
- } else {
- printf("unsupported algorithm: %s\n", algorithm_str);
- return -1;
- }
- return 0;
-}
-
-static int set_attestation_key_or_cert(uint32_t cmd, const xmlChar* algorithm_str,
- const xmlChar* format, const xmlChar* str) {
- int ret;
- keymaster_algorithm_t algorithm;
-
- ret = str_to_algorithm(&algorithm, algorithm_str);
- if (ret) {
- return ret;
- }
-
- if (xmlStrEqual(format, BAD_CAST "pem")) {
- ret = set_attestation_key_or_cert_pem(cmd, algorithm, str);
- } else if (xmlStrEqual(format, BAD_CAST "iecs")) {
- ret = set_attestation_key_or_cert_iecs(cmd, algorithm, str);
- } else {
- printf("unsupported key/cert format: %s\n", format);
- return -1;
- }
- return ret;
-}
-
-static int clear_cert_chain(const xmlChar* algorithm_str) {
- int ret;
- ClearAttestationCertChainRequest req;
- ClearAttestationCertChainResponse rsp;
-
- ret = str_to_algorithm(&req.algorithm, algorithm_str);
- if (ret) {
- return ret;
- }
-
- ret = trusty_keymaster_send(KM_CLEAR_ATTESTATION_CERT_CHAIN, req, &rsp);
- if (ret) {
- fprintf(stderr, "%s: trusty_keymaster_send failed %d\n", __func__, ret);
- return ret;
- }
- return 0;
-}
-
-static int process_xml(xmlTextReaderPtr xml) {
- int ret;
- const xmlChar* algorithm = NULL;
- const xmlChar* element = NULL;
- const xmlChar* element_format = NULL;
-
- while ((ret = xmlTextReaderRead(xml)) == 1) {
- int nodetype = xmlTextReaderNodeType(xml);
- const xmlChar *name, *value;
- name = xmlTextReaderConstName(xml);
- switch (nodetype) {
- case XML_READER_TYPE_ELEMENT:
- element = name;
- element_format = xmlTextReaderGetAttribute(xml, BAD_CAST "format");
- if (xmlStrEqual(name, BAD_CAST "Key")) {
- algorithm = xmlTextReaderGetAttribute(xml, BAD_CAST "algorithm");
- } else if (xmlStrEqual(name, BAD_CAST "CertificateChain")) {
- ret = clear_cert_chain(algorithm);
- if (ret) {
- fprintf(stderr, "%s, algorithm %s: Clear cert chain cmd failed, %d\n",
- element, algorithm, ret);
- return ret;
- }
- printf("%s, algorithm %s: Clear cert chain cmd done\n", element, algorithm);
- }
- break;
- case XML_READER_TYPE_TEXT:
- value = xmlTextReaderConstValue(xml);
- uint32_t cmd;
- if (xmlStrEqual(element, BAD_CAST "PrivateKey")) {
- if (xmlStrEqual(element_format, BAD_CAST "pem")) {
- cmd = KM_SET_ATTESTATION_KEY;
- } else if (xmlStrEqual(element_format, BAD_CAST "iecs")) {
- cmd = KM_SET_WRAPPED_ATTESTATION_KEY;
- } else {
- printf("unsupported key format: %s\n", element_format);
- return -1;
- }
- } else if (xmlStrEqual(element, BAD_CAST "Certificate")) {
- cmd = KM_APPEND_ATTESTATION_CERT_CHAIN;
- } else {
- break;
- }
-
- ret = set_attestation_key_or_cert(cmd, algorithm, element_format, value);
- if (ret) {
- fprintf(stderr, "%s, algorithm %s, format %s: Cmd 0x%x failed, %d\n", element,
- algorithm, element_format, cmd, ret);
- return ret;
- }
- printf("%s, algorithm %s, format %s: Cmd 0x%x done\n", element, algorithm,
- element_format, cmd);
- break;
- case XML_READER_TYPE_END_ELEMENT:
- element = NULL;
- break;
- }
- }
- return ret;
-}
-
-static int parse_xml_file(const char* filename) {
- int ret;
- xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);
- if (!xml) {
- fprintf(stderr, "failed to open %s\n", filename);
- return -1;
- }
-
- ret = process_xml(xml);
-
- xmlFreeTextReader(xml);
- if (ret != 0) {
- fprintf(stderr, "Failed to parse or process %s\n", filename);
- return -1;
- }
-
- return 0;
-}
-
-int main(int argc, char** argv) {
- int ret = 0;
-
- parse_options(argc, argv);
- if (optind + 1 != argc) {
- print_usage_and_exit(argv[0], EXIT_FAILURE);
- }
-
- ret = trusty_keymaster_connect();
- if (ret) {
- fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
- } else {
- ret = parse_xml_file(argv[optind]);
- trusty_keymaster_disconnect();
- }
-
- return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 086051d0e..8dba78d91 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -12,12 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
+cc_library {
+ name: "libtrusty",
+ vendor: true,
-cc_defaults {
- name: "libtrusty_defaults",
srcs: ["trusty.c"],
export_include_dirs: ["include"],
cflags: [
@@ -27,11 +25,3 @@ cc_defaults {
shared_libs: ["liblog"],
}
-
-cc_library {
- name: "libtrusty",
- // TODO(b/170753563): cc_fuzz can't deal with vendor components. Build
- // libtrusty for system and vendor.
- vendor_available: true,
- defaults: ["libtrusty_defaults"],
-}
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
deleted file mode 100644
index 1fa6fe4aa..000000000
--- a/trusty/libtrusty/include/trusty/ipc.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UAPI_LINUX_TRUSTY_IPC_H_
-#define _UAPI_LINUX_TRUSTY_IPC_H_
-
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#include <linux/uio.h>
-
-/**
- * enum transfer_kind - How to send an fd to Trusty
- * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
- * be mapped as nonsecure. Suitable for shared memory. The paired
- * fd must be a "memfd".
- * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will be
- * transitioned to "Secure" memory if Trusty is in TrustZone.
- * This transfer kind is suitable for donating video buffers or
- * other similar resources. The paired fd may need to come from a
- * platform-specific allocator for memory that may be
- * transitioned to "Secure".
- *
- * Describes how the user would like the resource in question to be sent to
- * Trusty. Options may be valid only for certain kinds of fds.
- */
-enum transfer_kind {
- TRUSTY_SHARE = 0,
- TRUSTY_LEND = 1,
-};
-
-/**
- * struct trusty_shm - Describes a transfer of memory to Trusty
- * @fd: The fd to transfer
- * @transfer: How to transfer it - see &enum transfer_kind
- */
-struct trusty_shm {
- __s32 fd;
- __u32 transfer;
-};
-
-/**
- * struct tipc_send_msg_req - Request struct for @TIPC_IOC_SEND_MSG
- * @iov: Pointer to an array of &struct iovec describing data to be sent
- * @shm: Pointer to an array of &struct trusty_shm describing any file
- * descriptors to be transferred.
- * @iov_cnt: Number of elements in the @iov array
- * @shm_cnt: Number of elements in the @shm array
- */
-struct tipc_send_msg_req {
- __u64 iov;
- __u64 shm;
- __u64 iov_cnt;
- __u64 shm_cnt;
-};
-
-#define TIPC_IOC_MAGIC 'r'
-#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char*)
-#define TIPC_IOC_SEND_MSG _IOW(TIPC_IOC_MAGIC, 0x81, struct tipc_send_msg_req)
-
-#if defined(CONFIG_COMPAT)
-#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, compat_uptr_t)
-#endif
-
-#endif
diff --git a/trusty/libtrusty/include/trusty/tipc.h b/trusty/libtrusty/include/trusty/tipc.h
index b44afd337..a3f2a3f61 100644
--- a/trusty/libtrusty/include/trusty/tipc.h
+++ b/trusty/libtrusty/include/trusty/tipc.h
@@ -21,11 +21,7 @@
extern "C" {
#endif
-#include <sys/uio.h>
-#include <trusty/ipc.h>
-
int tipc_connect(const char *dev_name, const char *srv_name);
-ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shm, int shmcnt);
int tipc_close(int fd);
#ifdef __cplusplus
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index c7a8ae1cf..9676b79fe 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_test {
name: "tipc-test",
vendor: true,
@@ -23,7 +19,6 @@ cc_test {
srcs: ["tipc_test.c"],
shared_libs: [
"libc",
- "libdmabufheap",
"liblog",
"libtrusty",
],
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 29c6f939f..d20d4eebf 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -21,12 +21,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
-#define __USE_GNU
-#include <sys/mman.h>
#include <sys/uio.h>
-#include <BufferAllocator/BufferAllocatorWrapper.h>
-
#include <trusty/tipc.h>
#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
@@ -43,7 +39,6 @@ static const char *closer1_name = "com.android.ipc-unittest.srv.closer1";
static const char *closer2_name = "com.android.ipc-unittest.srv.closer2";
static const char *closer3_name = "com.android.ipc-unittest.srv.closer3";
static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
-static const char* receiver_name = "com.android.trusty.memref.receiver";
static const char *_sopts = "hsvD:t:r:m:b:";
static const struct option _lopts[] = {
@@ -71,25 +66,25 @@ static const char *usage =
"\n"
;
-static const char* usage_long =
- "\n"
- "The following tests are available:\n"
- " connect - connect to datasink service\n"
- " connect_foo - connect to non existing service\n"
- " burst_write - send messages to datasink service\n"
- " echo - send/receive messages to echo service\n"
- " select - test select call\n"
- " blocked_read - test blocked read\n"
- " closer1 - connection closed by remote (test1)\n"
- " closer2 - connection closed by remote (test2)\n"
- " closer3 - connection closed by remote (test3)\n"
- " ta2ta-ipc - execute TA to TA unittest\n"
- " dev-uuid - print device uuid\n"
- " ta-access - test ta-access flags\n"
- " writev - writev test\n"
- " readv - readv test\n"
- " send-fd - transmit dma_buf to trusty, use as shm\n"
- "\n";
+static const char *usage_long =
+"\n"
+"The following tests are available:\n"
+" connect - connect to datasink service\n"
+" connect_foo - connect to non existing service\n"
+" burst_write - send messages to datasink service\n"
+" echo - send/receive messages to echo service\n"
+" select - test select call\n"
+" blocked_read - test blocked read\n"
+" closer1 - connection closed by remote (test1)\n"
+" closer2 - connection closed by remote (test2)\n"
+" closer3 - connection closed by remote (test3)\n"
+" ta2ta-ipc - execute TA to TA unittest\n"
+" dev-uuid - print device uuid\n"
+" ta-access - test ta-access flags\n"
+" writev - writev test\n"
+" readv - readv test\n"
+"\n"
+;
static uint opt_repeat = 1;
static uint opt_msgsize = 32;
@@ -890,77 +885,6 @@ static int readv_test(uint repeat, uint msgsz, bool var)
return 0;
}
-static int send_fd_test(void) {
- int ret;
- int dma_buf = -1;
- int fd = -1;
- volatile char* buf = MAP_FAILED;
- BufferAllocator* allocator = NULL;
-
- const size_t num_pages = 10;
-
- fd = tipc_connect(dev_name, receiver_name);
- if (fd < 0) {
- fprintf(stderr, "Failed to connect to test support TA - is it missing?\n");
- ret = -1;
- goto cleanup;
- }
-
- allocator = CreateDmabufHeapBufferAllocator();
- if (!allocator) {
- fprintf(stderr, "Failed to create dma-buf allocator.\n");
- ret = -1;
- goto cleanup;
- }
-
- size_t buf_size = PAGE_SIZE * num_pages;
- dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
- if (dma_buf < 0) {
- ret = dma_buf;
- fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
- goto cleanup;
- }
-
- buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
- if (buf == MAP_FAILED) {
- fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
- ret = -1;
- goto cleanup;
- }
-
- strcpy((char*)buf, "From NS");
-
- struct trusty_shm shm = {
- .fd = dma_buf,
- .transfer = TRUSTY_SHARE,
- };
-
- ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
- if (rc < 0) {
- fprintf(stderr, "tipc_send failed: %zd\n", rc);
- ret = rc;
- goto cleanup;
- }
- char c;
- read(fd, &c, 1);
- tipc_close(fd);
-
- ret = 0;
- for (size_t skip = 0; skip < num_pages; skip++) {
- ret |= strcmp("Hello from Trusty!", (const char*)&buf[skip * PAGE_SIZE]) ? (-1) : 0;
- }
-
-cleanup:
- if (buf != MAP_FAILED) {
- munmap((char*)buf, PAGE_SIZE);
- }
- close(dma_buf);
- if (allocator) {
- FreeDmabufHeapBufferAllocator(allocator);
- }
- tipc_close(fd);
- return ret;
-}
int main(int argc, char **argv)
{
@@ -1009,12 +933,10 @@ int main(int argc, char **argv)
rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
} else if (strcmp(test_name, "readv") == 0) {
rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
- } else if (strcmp(test_name, "send-fd") == 0) {
- rc = send_fd_test();
- } else {
- fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
- print_usage_and_exit(argv[0], EXIT_FAILURE, true);
- }
-
- return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ } else {
+ fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
+ print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+ }
+
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/trusty/libtrusty/tipc_ioctl.h b/trusty/libtrusty/tipc_ioctl.h
new file mode 100644
index 000000000..27da56a9e
--- /dev/null
+++ b/trusty/libtrusty/tipc_ioctl.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _TIPC_IOCTL_H
+#define _TIPC_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define TIPC_IOC_MAGIC 'r'
+#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
+
+#endif
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index f44f8b444..a6238af7d 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,47 +27,35 @@
#include <log/log.h>
-#include <trusty/ipc.h>
-
-int tipc_connect(const char* dev_name, const char* srv_name) {
- int fd;
- int rc;
-
- fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR));
- if (fd < 0) {
- rc = -errno;
- ALOGE("%s: cannot open tipc device \"%s\": %s\n", __func__, dev_name, strerror(errno));
- return rc < 0 ? rc : -1;
- }
-
- rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_CONNECT, srv_name));
- if (rc < 0) {
- rc = -errno;
- ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n", __func__, srv_name, errno);
- close(fd);
- return rc < 0 ? rc : -1;
- }
-
- ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
- return fd;
-}
-
-ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
- int shmcnt) {
- struct tipc_send_msg_req req;
- req.iov = (__u64)iov;
- req.iov_cnt = (__u64)iovcnt;
- req.shm = (__u64)shms;
- req.shm_cnt = (__u64)shmcnt;
-
- int rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_SEND_MSG, &req));
- if (rc < 0) {
- ALOGE("%s: failed to send message (err=%d)\n", __func__, rc);
- }
-
- return rc;
+#include "tipc_ioctl.h"
+
+int tipc_connect(const char *dev_name, const char *srv_name)
+{
+ int fd;
+ int rc;
+
+ fd = open(dev_name, O_RDWR);
+ if (fd < 0) {
+ rc = -errno;
+ ALOGE("%s: cannot open tipc device \"%s\": %s\n",
+ __func__, dev_name, strerror(errno));
+ return rc < 0 ? rc : -1;
+ }
+
+ rc = ioctl(fd, TIPC_IOC_CONNECT, srv_name);
+ if (rc < 0) {
+ rc = -errno;
+ ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n",
+ __func__, srv_name, errno);
+ close(fd);
+ return rc < 0 ? rc : -1;
+ }
+
+ ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
+ return fd;
}
-void tipc_close(int fd) {
- close(fd);
+void tipc_close(int fd)
+{
+ close(fd);
}
diff --git a/trusty/metrics/Android.bp b/trusty/metrics/Android.bp
deleted file mode 100644
index e0533bc3e..000000000
--- a/trusty/metrics/Android.bp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2021 The Android Open-Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library {
- name: "libtrusty_metrics",
- vendor: true,
- srcs: [
- "metrics.cpp",
- ],
- export_include_dirs: [
- "include",
- ],
- shared_libs: [
- "libbase",
- "liblog",
- "libtrusty",
- ],
-}
-
-cc_test {
- name: "libtrusty_metrics_test",
- vendor: true,
- srcs: [
- "metrics_test.cpp",
- ],
- static_libs: [
- "libtrusty_metrics",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libtrusty",
- ],
- require_root: true,
-}
diff --git a/trusty/metrics/include/trusty/metrics/metrics.h b/trusty/metrics/include/trusty/metrics/metrics.h
deleted file mode 100644
index 6949e9bed..000000000
--- a/trusty/metrics/include/trusty/metrics/metrics.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <string>
-
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace trusty {
-namespace metrics {
-
-using android::base::Result;
-using android::base::unique_fd;
-
-class TrustyMetrics {
- public:
- /* Wait for next event with a given timeout. Negative timeout means infinite timeout. */
- Result<void> WaitForEvent(int timeout_ms = -1);
- /* Attempt to handle an event from Metrics TA in a non-blocking manner. */
- Result<void> HandleEvent();
- /* Expose TIPC channel so that client can integrate it into an event loop with other fds. */
- int GetRawFd() { return metrics_fd_; };
-
- protected:
- TrustyMetrics(std::string tipc_dev) : tipc_dev_(std::move(tipc_dev)), metrics_fd_(-1) {}
- virtual ~TrustyMetrics(){};
-
- Result<void> Open();
- virtual void HandleCrash(const std::string& app_id) = 0;
- virtual void HandleEventDrop() = 0;
-
- private:
- std::string tipc_dev_;
- unique_fd metrics_fd_;
-};
-
-} // namespace metrics
-} // namespace trusty
-} // namespace android
diff --git a/trusty/metrics/include/trusty/metrics/tipc.h b/trusty/metrics/include/trusty/metrics/tipc.h
deleted file mode 100644
index 66d087684..000000000
--- a/trusty/metrics/include/trusty/metrics/tipc.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-/**
- * DOC: Metrics
- *
- * Metrics interface provides a way for Android to get Trusty metrics data.
- *
- * Currently, only "push" model is supported. Clients are expected to connect to
- * metrics service, listen for events, e.g. app crash events, and respond to
- * every event with a &struct metrics_req.
- *
- * Communication is driven by metrics service, i.e. requests/responses are all
- * sent from/to metrics service.
- *
- * Note that the type of the event is not known to the client ahead of time.
- *
- * In the future, if we need to have Android "pull" metrics data from Trusty,
- * that can be done by introducing a separate port.
- *
- * This interface is shared between Android and Trusty. There is a copy in each
- * repository. They must be kept in sync.
- */
-
-#define METRICS_PORT "com.android.trusty.metrics"
-
-/**
- * enum metrics_cmd - command identifiers for metrics interface
- * @METRICS_CMD_RESP_BIT: message is a response
- * @METRICS_CMD_REQ_SHIFT: number of bits used by @METRICS_CMD_RESP_BIT
- * @METRICS_CMD_REPORT_EVENT_DROP: report gaps in the event stream
- * @METRICS_CMD_REPORT_CRASH: report an app crash event
- */
-enum metrics_cmd {
- METRICS_CMD_RESP_BIT = 1,
- METRICS_CMD_REQ_SHIFT = 1,
-
- METRICS_CMD_REPORT_EVENT_DROP = (1 << METRICS_CMD_REQ_SHIFT),
- METRICS_CMD_REPORT_CRASH = (2 << METRICS_CMD_REQ_SHIFT),
-};
-
-/**
- * enum metrics_error - metrics error codes
- * @METRICS_NO_ERROR: no error
- * @METRICS_ERR_UNKNOWN_CMD: unknown or not implemented command
- */
-enum metrics_error {
- METRICS_NO_ERROR = 0,
- METRICS_ERR_UNKNOWN_CMD = 1,
-};
-
-/**
- * struct metrics_req - common structure for metrics requests
- * @cmd: command identifier - one of &enum metrics_cmd
- * @reserved: must be 0
- */
-struct metrics_req {
- uint32_t cmd;
- uint32_t reserved;
-} __attribute__((__packed__));
-
-/**
- * struct metrics_resp - common structure for metrics responses
- * @cmd: command identifier - %METRICS_CMD_RESP_BIT or'ed with a cmd in
- * one of &enum metrics_cmd
- * @status: response status, one of &enum metrics_error
- */
-struct metrics_resp {
- uint32_t cmd;
- uint32_t status;
-} __attribute__((__packed__));
-
-/**
- * struct metrics_report_crash_req - arguments of %METRICS_CMD_REPORT_CRASH
- * requests
- * @app_id_len: length of app ID that follows this structure
- */
-struct metrics_report_crash_req {
- uint32_t app_id_len;
-} __attribute__((__packed__));
-
-#define METRICS_MAX_APP_ID_LEN 256
-
-#define METRICS_MAX_MSG_SIZE \
- (sizeof(struct metrics_req) + sizeof(struct metrics_report_crash_req) + \
- METRICS_MAX_APP_ID_LEN)
diff --git a/trusty/metrics/metrics.cpp b/trusty/metrics/metrics.cpp
deleted file mode 100644
index 3ac128af0..000000000
--- a/trusty/metrics/metrics.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Sourete Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "metrics"
-
-#include <android-base/logging.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <trusty/metrics/metrics.h>
-#include <trusty/metrics/tipc.h>
-#include <trusty/tipc.h>
-#include <unistd.h>
-
-namespace android {
-namespace trusty {
-namespace metrics {
-
-using android::base::ErrnoError;
-using android::base::Error;
-
-Result<void> TrustyMetrics::Open() {
- int fd = tipc_connect(tipc_dev_.c_str(), METRICS_PORT);
- if (fd < 0) {
- return ErrnoError() << "failed to connect to Trusty metrics TA";
- }
-
- int flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0) {
- return ErrnoError() << "failed F_GETFL";
- }
-
- int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- if (rc < 0) {
- return ErrnoError() << "failed F_SETFL";
- }
-
- metrics_fd_.reset(fd);
- return {};
-}
-
-Result<void> TrustyMetrics::WaitForEvent(int timeout_ms) {
- if (!metrics_fd_.ok()) {
- return Error() << "connection to Metrics TA has not been initialized yet";
- }
-
- struct pollfd pfd = {
- .fd = metrics_fd_,
- .events = POLLIN,
- };
-
- int rc = poll(&pfd, 1, timeout_ms);
- if (rc != 1) {
- return ErrnoError() << "failed poll()";
- }
-
- if (!(pfd.revents & POLLIN)) {
- return ErrnoError() << "channel not ready";
- }
-
- return {};
-}
-
-Result<void> TrustyMetrics::HandleEvent() {
- if (!metrics_fd_.ok()) {
- return Error() << "connection to Metrics TA has not been initialized yet";
- }
-
- uint8_t msg[METRICS_MAX_MSG_SIZE];
-
- auto rc = read(metrics_fd_, msg, sizeof(msg));
- if (rc < 0) {
- return ErrnoError() << "failed to read metrics message";
- }
- size_t msg_len = rc;
-
- if (msg_len < sizeof(metrics_req)) {
- return Error() << "message too small: " << rc;
- }
- auto req = reinterpret_cast<metrics_req*>(msg);
- size_t offset = sizeof(metrics_req);
- uint32_t status = METRICS_NO_ERROR;
-
- switch (req->cmd) {
- case METRICS_CMD_REPORT_CRASH: {
- if (msg_len < offset + sizeof(metrics_report_crash_req)) {
- return Error() << "message too small: " << rc;
- }
- auto crash_args = reinterpret_cast<metrics_report_crash_req*>(msg + offset);
- offset += sizeof(metrics_report_crash_req);
-
- if (msg_len < offset + crash_args->app_id_len) {
- return Error() << "message too small: " << rc;
- }
- auto app_id_ptr = reinterpret_cast<char*>(msg + offset);
- std::string app_id(app_id_ptr, crash_args->app_id_len);
-
- HandleCrash(app_id);
- break;
- }
-
- case METRICS_CMD_REPORT_EVENT_DROP:
- HandleEventDrop();
- break;
-
- default:
- status = METRICS_ERR_UNKNOWN_CMD;
- break;
- }
-
- metrics_resp resp = {
- .cmd = req->cmd | METRICS_CMD_RESP_BIT,
- .status = status,
- };
-
- rc = write(metrics_fd_, &resp, sizeof(resp));
- if (rc < 0) {
- return ErrnoError() << "failed to request next metrics event";
- }
-
- if (rc != (int)sizeof(resp)) {
- return Error() << "unexpected number of bytes sent event: " << rc;
- }
-
- return {};
-}
-
-} // namespace metrics
-} // namespace trusty
-} // namespace android
diff --git a/trusty/metrics/metrics_test.cpp b/trusty/metrics/metrics_test.cpp
deleted file mode 100644
index 407ddf2c0..000000000
--- a/trusty/metrics/metrics_test.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/unique_fd.h>
-#include <binder/IPCThreadState.h>
-#include <gtest/gtest.h>
-#include <poll.h>
-#include <trusty/metrics/metrics.h>
-#include <trusty/tipc.h>
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define CRASHER_PORT "com.android.trusty.metrics.test.crasher"
-
-namespace android {
-namespace trusty {
-namespace metrics {
-
-using android::base::unique_fd;
-
-static void TriggerCrash() {
- size_t num_retries = 3;
- int fd = -1;
-
- for (size_t i = 0; i < num_retries; i++) {
- /* It's possible to time out waiting for crasher TA to restart. */
- fd = tipc_connect(TIPC_DEV, CRASHER_PORT);
- if (fd >= 0) {
- break;
- }
- }
-
- unique_fd crasher(fd);
- ASSERT_GE(crasher, 0);
-
- int msg = 0;
- int rc = write(crasher, &msg, sizeof(msg));
- ASSERT_EQ(rc, sizeof(msg));
-}
-
-class TrustyMetricsTest : public TrustyMetrics, public ::testing::Test {
- public:
- TrustyMetricsTest() : TrustyMetrics(TIPC_DEV) {}
-
- virtual void HandleCrash(const std::string& app_id) override { crashed_app_ = app_id; }
-
- virtual void HandleEventDrop() override { event_drop_count_++; }
-
- virtual void SetUp() override {
- auto ret = Open();
- ASSERT_TRUE(ret.ok()) << ret.error();
- }
-
- void WaitForAndHandleEvent() {
- auto ret = WaitForEvent(30000 /* 30 second timeout */);
- ASSERT_TRUE(ret.ok()) << ret.error();
-
- ret = HandleEvent();
- ASSERT_TRUE(ret.ok()) << ret.error();
- }
-
- std::string crashed_app_;
- size_t event_drop_count_;
-};
-
-TEST_F(TrustyMetricsTest, Crash) {
- TriggerCrash();
- WaitForAndHandleEvent();
-
- /* Check that correct TA crashed. */
- ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
-}
-
-TEST_F(TrustyMetricsTest, PollSet) {
- int binder_fd;
- int rc = IPCThreadState::self()->setupPolling(&binder_fd);
- ASSERT_EQ(rc, 0);
- ASSERT_GE(binder_fd, 0);
-
- TriggerCrash();
-
- struct pollfd pfds[] = {
- {
- .fd = binder_fd,
- .events = POLLIN,
- },
- {
- .fd = GetRawFd(),
- .events = POLLIN,
- },
- };
-
- rc = poll(pfds, 2, 30000 /* 30 second timeout */);
- /* We expect one event on the metrics fd. */
- ASSERT_EQ(rc, 1);
- ASSERT_TRUE(pfds[1].revents & POLLIN);
-
- auto ret = HandleEvent();
- ASSERT_TRUE(ret.ok()) << ret.error();
-
- /* Check that correct TA crashed. */
- ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
-}
-
-TEST_F(TrustyMetricsTest, EventDrop) {
- /* We know the size of the internal event queue is less than this. */
- size_t num_events = 3;
-
- ASSERT_EQ(event_drop_count_, 0);
-
- for (auto i = 0; i < num_events; i++) {
- TriggerCrash();
- }
-
- for (auto i = 0; i < num_events; i++) {
- WaitForAndHandleEvent();
- if (event_drop_count_ > 0) {
- break;
- }
- }
-
- ASSERT_EQ(event_drop_count_, 1);
-}
-
-} // namespace metrics
-} // namespace trusty
-} // namespace android
diff --git a/trusty/secure_dpu/Android.bp b/trusty/secure_dpu/Android.bp
deleted file mode 100644
index 39dd0b969..000000000
--- a/trusty/secure_dpu/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_headers {
- name: "secure_dpu_headers",
- vendor: true,
-
- export_include_dirs: ["include"],
-}
diff --git a/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h b/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
deleted file mode 100644
index b939d44aa..000000000
--- a/trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2020, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-/**
- * DOC: Secure DPU
- *
- * The Secure DPU works as the persistent channel between the non-secure and the
- * secure world. The channel is established during the boot up stage of the
- * non-secure world system. In general, the established channel allows the
- * secure world applications initiate requests or notifications to the non-secure
- * world.
- *
- * For particular devices, the secure world can only perform operations on the
- * display when in the TUI session if device-specific setup is done by the
- * non-secure world. Besides, the non-secure world could allocate framebuffer
- * for the secure world application if the memory is limited in the secure world
- * on specific devices.
- *
- * Currently, supported requests are to start / stop the secure display mode and
- * to allocate framebuffer.
- *
- * This header file needs to be synced on both the Trusty and the Android
- * codebase.
- */
-
-#define SECURE_DPU_PORT_NAME "com.android.trusty.secure_dpu"
-#define SECURE_DPU_MAX_MSG_SIZE 64
-
-/**
- * enum secure_dpu_cmd - command identifiers for secure_fb interface
- * @SECURE_DPU_CMD_RESP_BIT:
- * Message is a response.
- * @SECURE_DPU_CMD_REQ_SHIFT:
- * Number of bits used by @SECURE_DPU_CMD_RESP_BIT.
- * @SECURE_DPU_CMD_START_SECURE_DISPLAY:
- * Notify the system to start secure display mode
- * @SECURE_DPU_CMD_STOP_SECURE_DISPLAY:
- * Notify the system to stop secure display mode
- * @SECURE_DPU_CMD_ALLOCATE_BUFFER:
- * Request non-secure world to allocate the buffer
- */
-enum secure_dpu_cmd {
- SECURE_DPU_CMD_RESP_BIT = 1,
- SECURE_DPU_CMD_REQ_SHIFT = 1,
- SECURE_DPU_CMD_START_SECURE_DISPLAY = (1 << SECURE_DPU_CMD_REQ_SHIFT),
- SECURE_DPU_CMD_STOP_SECURE_DISPLAY = (2 << SECURE_DPU_CMD_REQ_SHIFT),
- SECURE_DPU_CMD_ALLOCATE_BUFFER = (3 << SECURE_DPU_CMD_REQ_SHIFT),
-};
-
-/**
- * struct secure_dpu_allocate_buffer_req - payload for
- * %SECURE_DPU_CMD_ALLOCATE_BUFFER
- * request
- * @buffer_len: Requested length
- */
-struct secure_dpu_allocate_buffer_req {
- uint64_t buffer_len;
-};
-
-/**
- * struct secure_dpu_allocate_buffer_resp - payload for
- * %SECURE_DPU_CMD_ALLOCATE_BUFFER
- * response
- * @buffer_len: Allocated length
- */
-struct secure_dpu_allocate_buffer_resp {
- uint64_t buffer_len;
-};
-
-/**
- * struct secure_fb_req - common structure for secure_fb requests.
- * @cmd: Command identifier - one of &enum secure_dpu_cmd.
- */
-struct secure_dpu_req {
- uint32_t cmd;
-};
-
-/**
- * struct secure_dpu_resp - common structure for secure_dpu responses.
- * @cmd: Command identifier - %SECURE_DPU_CMD_RESP_BIT or'ed with the
- * command identifier of the corresponding
- * request.
- * @status: Status of requested operation. One of &enum secure_dpu_error.
- */
-struct secure_dpu_resp {
- uint32_t cmd;
- int32_t status;
-};
-
-enum secure_dpu_error {
- SECURE_DPU_ERROR_OK = 0,
- SECURE_DPU_ERROR_FAIL = -1,
- SECURE_DPU_ERROR_UNINITIALIZED = -2,
- SECURE_DPU_ERROR_PARAMETERS = -3,
- SECURE_DPU_ERROR_NO_MEMORY = -4,
-};
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
index d031b0c1e..18b4a5f90 100644
--- a/trusty/storage/interface/Android.bp
+++ b/trusty/storage/interface/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libtrustystorageinterface",
vendor: true,
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
index f28308b04..2fba17edd 100644
--- a/trusty/storage/lib/Android.bp
+++ b/trusty/storage/lib/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_static {
name: "libtrustystorage",
vendor: true,
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index d67089fb2..b93facbbc 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "storageproxyd",
vendor: true,
@@ -29,10 +25,7 @@ cc_binary {
"proxy.c",
],
- shared_libs: [
- "liblog",
- "libhardware_legacy",
- ],
+ shared_libs: ["liblog"],
header_libs: ["libcutils_headers"],
static_libs: [
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index f059935bf..7dfd0d01b 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -16,10 +16,7 @@
#include <errno.h>
#include <fcntl.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_proto.h>
#include <scsi/sg.h>
-#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -32,8 +29,6 @@
#include <linux/major.h>
#include <linux/mmc/ioctl.h>
-#include <hardware_legacy/power.h>
-
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
@@ -58,17 +53,6 @@
#define MMC_BLOCK_SIZE 512
/*
- * Number of retry attempts when an RPMB authenticated write triggers a UNIT
- * ATTENTION
- */
-#define UFS_RPMB_WRITE_RETRY_COUNT 1
-/*
- * Number of retry attempts when an RPMB read operation triggers a UNIT
- * ATTENTION
- */
-#define UFS_RPMB_READ_RETRY_COUNT 3
-
-/*
* There should be no timeout for security protocol ioctl call, so we choose a
* large number for timeout.
* 20000 millisecs == 20 seconds
@@ -116,65 +100,22 @@ static int rpmb_fd = -1;
static uint8_t read_buf[4096];
static enum dev_type dev_type = UNKNOWN_RPMB;
-static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
+#ifdef RPMB_DEBUG
-/**
- * log_buf - Log a byte buffer to the android log.
- * @priority: One of ANDROID_LOG_* priority levels from android_LogPriority in
- * android/log.h
- * @prefix: A null-terminated string that identifies this buffer. Must be less
- * than 128 bytes.
- * @buf: Buffer to dump.
- * @size: Length of @buf in bytes.
- */
-#define LOG_BUF_SIZE 256
-static int log_buf(int priority, const char* prefix, const uint8_t* buf, size_t size) {
- int rc;
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
size_t i;
- char line[LOG_BUF_SIZE] = {0};
- char* cur = line;
- rc = snprintf(line, LOG_BUF_SIZE, "%s @%p [%zu]", prefix, buf, size);
- if (rc < 0 || rc >= LOG_BUF_SIZE) {
- goto err;
- }
- cur += rc;
+ printf("%s @%p [%zu]", prefix, buf, size);
for (i = 0; i < size; i++) {
- if (i % 32 == 0) {
- /*
- * Flush the line out to the log after we have printed 32 bytes
- * (also flushes the header line on the first iteration and sets up
- * for printing the buffer itself)
- */
- LOG_PRI(priority, LOG_TAG, "%s", line);
- memset(line, 0, LOG_BUF_SIZE);
- cur = line;
- /* Shift output over by the length of the prefix */
- rc = snprintf(line, LOG_BUF_SIZE, "%*s", (int)strlen(prefix), "");
- if (rc < 0 || rc >= LOG_BUF_SIZE) {
- goto err;
- }
- cur += rc;
- }
- rc = snprintf(cur, LOG_BUF_SIZE - (cur - line), "%02x ", buf[i]);
- if (rc < 0 || rc >= LOG_BUF_SIZE - (cur - line)) {
- goto err;
- }
- cur += rc;
- }
- LOG_PRI(priority, LOG_TAG, "%s", line);
-
- return 0;
-
-err:
- if (rc < 0) {
- return rc;
- } else {
- ALOGE("log_buf prefix was too long");
- return -1;
+ if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
+ printf(" %02x", buf[i]);
}
+ printf("\n");
+ fflush(stdout);
}
+#endif
+
static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,
unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,
unsigned char* cmdp, void* sbp) {
@@ -190,137 +131,6 @@ static void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned ch
io_hdrp->timeout = TIMEOUT;
}
-/**
- * enum scsi_result - Results of checking the SCSI status and sense buffer
- *
- * @SCSI_RES_OK: SCSI status and sense are good
- * @SCSI_RES_ERR: SCSI status or sense contain an unhandled error
- * @SCSI_RES_RETRY: SCSI sense buffer contains a status that indicates that the
- * command should be retried
- */
-enum scsi_result {
- SCSI_RES_OK = 0,
- SCSI_RES_ERR,
- SCSI_RES_RETRY,
-};
-
-static enum scsi_result check_scsi_sense(const uint8_t* sense_buf, size_t len) {
- uint8_t response_code = 0;
- uint8_t sense_key = 0;
- uint8_t additional_sense_code = 0;
- uint8_t additional_sense_code_qualifier = 0;
- uint8_t additional_length = 0;
-
- if (!sense_buf || len == 0) {
- ALOGE("Invalid SCSI sense buffer, length: %zu\n", len);
- return SCSI_RES_ERR;
- }
-
- response_code = 0x7f & sense_buf[0];
-
- if (response_code < 0x70 || response_code > 0x73) {
- ALOGE("Invalid SCSI sense response code: %hhu\n", response_code);
- return SCSI_RES_ERR;
- }
-
- if (response_code >= 0x72) {
- /* descriptor format, SPC-6 4.4.2 */
- if (len > 1) {
- sense_key = 0xf & sense_buf[1];
- }
- if (len > 2) {
- additional_sense_code = sense_buf[2];
- }
- if (len > 3) {
- additional_sense_code_qualifier = sense_buf[3];
- }
- if (len > 7) {
- additional_length = sense_buf[7];
- }
- } else {
- /* fixed format, SPC-6 4.4.3 */
- if (len > 2) {
- sense_key = 0xf & sense_buf[2];
- }
- if (len > 7) {
- additional_length = sense_buf[7];
- }
- if (len > 12) {
- additional_sense_code = sense_buf[12];
- }
- if (len > 13) {
- additional_sense_code_qualifier = sense_buf[13];
- }
- }
-
- switch (sense_key) {
- case NO_SENSE:
- case 0x0f: /* COMPLETED, not present in kernel headers */
- ALOGD("SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
- additional_sense_code, additional_sense_code_qualifier);
- return SCSI_RES_OK;
- case UNIT_ATTENTION:
- ALOGD("UNIT ATTENTION with sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
- additional_sense_code, additional_sense_code_qualifier);
- if (additional_sense_code == 0x29) {
- /* POWER ON or RESET condition */
- return SCSI_RES_RETRY;
- }
-
- /* treat this UNIT ATTENTION as an error if we don't recognize it */
- break;
- }
-
- ALOGE("Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\n", sense_key,
- additional_sense_code, additional_sense_code_qualifier);
- log_buf(ANDROID_LOG_ERROR, "sense buffer: ", sense_buf, len);
- return SCSI_RES_ERR;
-}
-
-static enum scsi_result check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {
- if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) {
- return SCSI_RES_OK;
- }
-
- if (io_hdrp->status & 0x01) {
- ALOGE("SG_IO received unknown status, LSB is set: %hhu", io_hdrp->status);
- }
-
- if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {
- enum scsi_result scsi_res = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);
- if (scsi_res == SCSI_RES_RETRY) {
- return SCSI_RES_RETRY;
- } else if (scsi_res != SCSI_RES_OK) {
- ALOGE("Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: "
- "%hu\n",
- io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
- return scsi_res;
- }
- }
-
- switch (io_hdrp->masked_status) {
- case GOOD:
- break;
- case CHECK_CONDITION:
- /* handled by check_sg_sense above */
- break;
- default:
- ALOGE("SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\n",
- io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);
- return SCSI_RES_ERR;
- }
-
- if (io_hdrp->host_status != 0) {
- ALOGE("SG_IO failed with host_status: %hu, driver_status: %hu\n", io_hdrp->host_status,
- io_hdrp->driver_status);
- }
-
- if (io_hdrp->resid != 0) {
- ALOGE("SG_IO resid was non-zero: %d\n", io_hdrp->resid);
- }
- return SCSI_RES_ERR;
-}
-
static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
@@ -339,7 +149,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->reliable_write_size);
+ print_buf("request: ", write_buf, req->reliable_write_size);
#endif
write_buf += req->reliable_write_size;
mmc.multi.num_of_cmds++;
@@ -355,7 +165,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req
mmc_ioc_cmd_set_data((*cmd), write_buf);
#ifdef RPMB_DEBUG
ALOGI("opcode: 0x%x, write_flag: 0x%x\n", cmd->opcode, cmd->write_flag);
- log_buf(ANDROID_LOG_INFO, "request: ", write_buf, req->write_size);
+ print_buf("request: ", write_buf, req->write_size);
#endif
write_buf += req->write_size;
mmc.multi.num_of_cmds++;
@@ -384,7 +194,6 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req
static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
int rc;
- int wl_rc;
const uint8_t* write_buf = req->payload;
/*
* Meaning of member values are stated on the definition of struct sec_proto_cdb.
@@ -393,60 +202,36 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
unsigned char sense_buffer[32];
- bool is_request_write = req->reliable_write_size > 0;
-
- wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
- if (wl_rc < 0) {
- ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
- return wl_rc;
- }
-
if (req->reliable_write_size) {
/* Prepare SECURITY PROTOCOL OUT command. */
+ out_cdb.length = __builtin_bswap32(req->reliable_write_size);
sg_io_hdr_t io_hdr;
- int retry_count = UFS_RPMB_WRITE_RETRY_COUNT;
- do {
- out_cdb.length = __builtin_bswap32(req->reliable_write_size);
- set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
- req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
- sense_buffer);
- rc = ioctl(sg_fd, SG_IO, &io_hdr);
- if (rc < 0) {
- ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
- goto err_op;
- }
- } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);
+ set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+ req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
+ sense_buffer);
+ rc = ioctl(sg_fd, SG_IO, &io_hdr);
+ if (rc < 0) {
+ ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ }
write_buf += req->reliable_write_size;
}
if (req->write_size) {
/* Prepare SECURITY PROTOCOL OUT command. */
+ out_cdb.length = __builtin_bswap32(req->write_size);
sg_io_hdr_t io_hdr;
- /*
- * We don't retry write response request messages (is_request_write ==
- * true) because a unit attention condition between the write and
- * requesting a response means that the device was reset and we can't
- * get a response to our original write. We can only retry this SG_IO
- * call when it is the first call in our sequence.
- */
- int retry_count = is_request_write ? 0 : UFS_RPMB_READ_RETRY_COUNT;
- do {
- out_cdb.length = __builtin_bswap32(req->write_size);
- set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
- req->write_size, (void*)write_buf, (unsigned char*)&out_cdb,
- sense_buffer);
- rc = ioctl(sg_fd, SG_IO, &io_hdr);
- if (rc < 0) {
- ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
- goto err_op;
- }
- } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);
+ set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
+ req->write_size, (void*)write_buf, (unsigned char*)&out_cdb, sense_buffer);
+ rc = ioctl(sg_fd, SG_IO, &io_hdr);
+ if (rc < 0) {
+ ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ }
write_buf += req->write_size;
}
if (req->read_size) {
/* Prepare SECURITY PROTOCOL IN command. */
- in_cdb.length = __builtin_bswap32(req->read_size);
+ out_cdb.length = __builtin_bswap32(req->read_size);
sg_io_hdr_t io_hdr;
set_sg_io_hdr(&io_hdr, SG_DXFER_FROM_DEV, sizeof(in_cdb), sizeof(sense_buffer),
req->read_size, read_buf, (unsigned char*)&in_cdb, sense_buffer);
@@ -454,15 +239,7 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
if (rc < 0) {
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
}
- check_sg_io_hdr(&io_hdr);
- }
-
-err_op:
- wl_rc = release_wake_lock(UFS_WAKE_LOCK_NAME);
- if (wl_rc < 0) {
- ALOGE("%s: failed to release wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
}
-
return rc;
}
@@ -556,7 +333,7 @@ int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size) log_buf(ANDROID_LOG_INFO, "response: ", read_buf, req->read_size);
+ if (req->read_size) print_buf("response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 2fde30f84..5b83e2141 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -477,6 +477,7 @@ int storage_init(const char *dirname)
if (ssdir_fd < 0) {
ALOGE("failed to open ss root dir \"%s\": %s\n",
dirname, strerror(errno));
+ return -1;
}
ssdir_name = dirname;
return 0;
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
index 891f543c4..536c3ca7a 100644
--- a/trusty/storage/tests/Android.bp
+++ b/trusty/storage/tests/Android.bp
@@ -14,10 +14,6 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_test {
name: "secure-storage-unit-test",
vendor: true,
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index b42d665d8..fd8daa84a 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -23,9 +23,8 @@
# HAL loading of gatekeeper.trusty.
PRODUCT_PACKAGES += \
- android.hardware.security.keymint-service.trusty \
- android.hardware.gatekeeper@1.0-service.trusty \
- trusty_apploader
+ android.hardware.keymaster@4.0-service.trusty \
+ android.hardware.gatekeeper@1.0-service.trusty
PRODUCT_PROPERTY_OVERRIDES += \
ro.hardware.keystore=trusty \
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
deleted file mode 100644
index 74106ec58..000000000
--- a/trusty/trusty-test.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (C) 2020 The Android Open-Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-PRODUCT_PACKAGES += \
- spiproxyd \
- trusty_keymaster_set_attestation_key \
- keymaster_soft_attestation_keys.xml \
-
diff --git a/trusty/utils/acvp/Android.bp b/trusty/utils/acvp/Android.bp
deleted file mode 100644
index b851e39c4..000000000
--- a/trusty/utils/acvp/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at //
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "trusty_acvp_modulewrapper",
- vendor: true,
-
- srcs: [
- "trusty_modulewrapper.cpp",
- ],
- static_libs: [
- "libacvp_modulewrapper",
- ],
- shared_libs: [
- "libbase",
- "libc",
- "libdmabufheap",
- "liblog",
- "libtrusty",
- "libssl",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/trusty/utils/acvp/acvp_ipc.h b/trusty/utils/acvp/acvp_ipc.h
deleted file mode 100644
index 8b48ae3cd..000000000
--- a/trusty/utils/acvp/acvp_ipc.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ACVP_PORT "com.android.trusty.acvp"
-
-/*
- * Maximum number of arguments
- */
-#define ACVP_MAX_NUM_ARGUMENTS 8
-
-/*
- * Maximum length of an algorithm name
- */
-#define ACVP_MAX_NAME_LENGTH 30
-
-/*
- * Maximum length of an ACVP request message
- */
-#define ACVP_MAX_MESSAGE_LENGTH sizeof(struct acvp_req)
-
-/*
- * Minimum length of the shared memory buffer
- *
- * This must be at least as long as the longest reply from the ACVP service
- * (currently the reply from getConfig()).
- */
-#define ACVP_MIN_SHARED_MEMORY 16384
-
-/**
- * acvp_req - Request for the Trusty ACVP app
- * @num_args: Number of acvp_arg structures following this struct
- * @buffer_size: Total size of shared memory buffer
- * @lengths: Length of each argument in the shared memory buffer
- *
- * @num_args copies of the acvp_arg struct follow this structure.
- */
-struct acvp_req {
- uint32_t num_args;
- uint32_t buffer_size;
- uint32_t lengths[ACVP_MAX_NUM_ARGUMENTS];
-};
-
-/**
- * acvp_resp - Response to a ACVP request
- *
- * @num_spans: Number of response sections
- * @lengths: Length of each response section
- */
-struct acvp_resp {
- uint32_t num_spans;
- uint32_t lengths[ACVP_MAX_NUM_ARGUMENTS];
-};
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
diff --git a/trusty/utils/acvp/trusty_modulewrapper.cpp b/trusty/utils/acvp/trusty_modulewrapper.cpp
deleted file mode 100644
index 70ffb52ec..000000000
--- a/trusty/utils/acvp/trusty_modulewrapper.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "TrustyAcvpModulewrapper"
-
-#include <BufferAllocator/BufferAllocator.h>
-#include <android-base/file.h>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
-#include <errno.h>
-#include <log/log.h>
-#include <modulewrapper.h>
-#include <openssl/span.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <trusty/tipc.h>
-#include <unistd.h>
-#include <iostream>
-
-#include "acvp_ipc.h"
-
-constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::base::unique_fd;
-using android::base::WriteFully;
-
-static inline size_t AlignUpToPage(size_t size) {
- return (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
-namespace {
-
-class ModuleWrapper {
- private:
- static const char* kAcvpPort_;
- static const char* kTrustyDeviceName_;
-
- public:
- ModuleWrapper();
- ~ModuleWrapper();
-
- Result<void> SendMessage(bssl::Span<const bssl::Span<const uint8_t>>);
-
- Result<void> ForwardResponse();
-
- private:
- // Connection to the Trusty ACVP service
- int tipc_fd_ = -1;
-
- // Shared memory DMA buf
- unique_fd dmabuf_fd_;
-
- // Size of shared memory mapping
- size_t shm_size_ = 0;
-
- // Shared memory mapping
- uint8_t* shm_buffer_ = nullptr;
-};
-
-} // namespace
-
-const char* ModuleWrapper::kAcvpPort_ = ACVP_PORT;
-const char* ModuleWrapper::kTrustyDeviceName_ = kTrustyDeviceName;
-
-ModuleWrapper::ModuleWrapper() {
- tipc_fd_ = tipc_connect(kTrustyDeviceName_, kAcvpPort_);
- if (tipc_fd_ < 0) {
- fprintf(stderr, "Failed to connect to Trusty ACVP test app: %s\n", strerror(-tipc_fd_));
- }
-}
-
-ModuleWrapper::~ModuleWrapper() {
- if (tipc_fd_ >= 0) {
- tipc_close(tipc_fd_);
- }
-
- if (shm_buffer_) {
- munmap(shm_buffer_, shm_size_);
- }
-}
-
-Result<void> ModuleWrapper::SendMessage(bssl::Span<const bssl::Span<const uint8_t>> args) {
- assert(args.size() < ACVP_MAX_NUM_ARGUMENTS);
- assert(args[0].size() < ACVP_MAX_NAME_LENGTH);
-
- struct acvp_req request;
- request.num_args = args.size();
-
- size_t total_args_size = 0;
- for (auto arg : args) {
- total_args_size += arg.size();
- }
-
- shm_size_ = ACVP_MIN_SHARED_MEMORY;
- if (total_args_size > shm_size_) {
- shm_size_ = AlignUpToPage(total_args_size);
- }
- request.buffer_size = shm_size_;
-
- struct iovec iov = {
- .iov_base = &request,
- .iov_len = sizeof(struct acvp_req),
- };
-
- BufferAllocator alloc;
- dmabuf_fd_.reset(alloc.Alloc(kDmabufSystemHeapName, shm_size_));
- if (!dmabuf_fd_.ok()) {
- return ErrnoError() << "Error creating dmabuf";
- }
-
- shm_buffer_ = (uint8_t*)mmap(0, shm_size_, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd_, 0);
- if (shm_buffer_ == MAP_FAILED) {
- return ErrnoError() << "Failed to map shared memory dmabuf";
- }
-
- size_t cur_offset = 0;
- for (int i = 0; i < args.size(); ++i) {
- request.lengths[i] = args[i].size();
- memcpy(shm_buffer_ + cur_offset, args[i].data(), args[i].size());
- cur_offset += args[i].size();
- }
-
- struct trusty_shm shm = {
- .fd = dmabuf_fd_.get(),
- .transfer = TRUSTY_SHARE,
- };
-
- int rc = tipc_send(tipc_fd_, &iov, 1, &shm, 1);
- if (rc != sizeof(struct acvp_req)) {
- return ErrnoError() << "Failed to send request to Trusty ACVP service";
- }
-
- return {};
-}
-
-Result<void> ModuleWrapper::ForwardResponse() {
- struct acvp_resp resp;
- int bytes_read = read(tipc_fd_, &resp, sizeof(struct acvp_resp));
- if (bytes_read < 0) {
- return ErrnoError() << "Failed to read response from Trusty ACVP service";
- }
-
- if (bytes_read != sizeof(struct acvp_resp)) {
- return Error() << "Trusty ACVP response overflowed expected size";
- }
-
- size_t total_args_size = 0;
- for (size_t i = 0; i < resp.num_spans; i++) {
- total_args_size += resp.lengths[i];
- }
-
- iovec iovs[2];
- iovs[0].iov_base = &resp;
- iovs[0].iov_len = sizeof(uint32_t) * (1 + resp.num_spans);
-
- iovs[1].iov_base = shm_buffer_;
- iovs[1].iov_len = total_args_size;
-
- size_t iov_done = 0;
- while (iov_done < 2) {
- ssize_t r;
- do {
- r = writev(STDOUT_FILENO, &iovs[iov_done], 2 - iov_done);
- } while (r == -1 && errno == EINTR);
-
- if (r <= 0) {
- return Error() << "Failed to write ACVP response to standard out";
- }
-
- size_t written = r;
- for (size_t i = iov_done; i < 2 && written > 0; i++) {
- iovec& iov = iovs[i];
-
- size_t done = written;
- if (done > iov.iov_len) {
- done = iov.iov_len;
- }
-
- iov.iov_base = reinterpret_cast<uint8_t*>(iov.iov_base) + done;
- iov.iov_len -= done;
- written -= done;
-
- if (iov.iov_len == 0) {
- iov_done++;
- }
- }
-
- assert(written == 0);
- }
-
- return {};
-}
-
-int main() {
- for (;;) {
- auto buffer = bssl::acvp::RequestBuffer::New();
- auto args = bssl::acvp::ParseArgsFromFd(STDIN_FILENO, buffer.get());
- if (args.empty()) {
- ALOGE("Could not parse arguments\n");
- return EXIT_FAILURE;
- }
-
- ModuleWrapper wrapper;
- auto res = wrapper.SendMessage(args);
- if (!res.ok()) {
- std::cerr << res.error() << std::endl;
- return EXIT_FAILURE;
- }
-
- res = wrapper.ForwardResponse();
- if (!res.ok()) {
- std::cerr << res.error() << std::endl;
- return EXIT_FAILURE;
- }
- }
-
- return EXIT_SUCCESS;
-};
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
index c5853efcb..e923e829c 100644
--- a/trusty/utils/rpmb_dev/Android.bp
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -11,16 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "system_core_trusty_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- default_applicable_licenses: ["system_core_trusty_license"],
-}
-
cc_binary {
name: "rpmb_dev",
vendor: true,
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index 202562114..af97eba9f 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -283,7 +283,6 @@ static struct rpmb_dev_cmd rpmb_dev_cmd_table[] = {
{
.func = rpmb_dev_data_read,
.resp = RPMB_RESP_DATA_READ,
- .check_key_programmed = true,
.check_addr = true,
.multi_packet_res = true,
.res_mac = true,
@@ -592,7 +591,7 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS;
}
- open_flags = O_RDWR | O_SYNC;
+ open_flags = O_RDWR;
if (init) {
open_flags |= O_CREAT | O_TRUNC;
}
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.rc b/trusty/utils/rpmb_dev/rpmb_dev.rc
index 9e203b89c..9f60e8133 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.rc
+++ b/trusty/utils/rpmb_dev/rpmb_dev.rc
@@ -16,7 +16,7 @@ service storageproxyd /vendor/bin/storageproxyd -d /dev/trusty-ipc-dev0 \
disabled
user root
-service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --size 2048
+service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --key "ea df 64 44 ea 65 5d 1c 87 27 d4 20 71 0d 53 42 dd 73 a3 38 63 e1 d7 94 c3 72 a6 ea e0 64 64 e6" --size 2048
disabled
user system
group system
diff --git a/trusty/utils/spiproxyd/Android.bp b/trusty/utils/spiproxyd/Android.bp
deleted file mode 100644
index 81f3e73e5..000000000
--- a/trusty/utils/spiproxyd/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2020 The Android Open-Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "spiproxyd",
- vendor: true,
-
- srcs: [
- "main.c",
- ],
-
- shared_libs: [
- "liblog",
- "libtrusty",
- ],
-
- init_rc: [
- "proxy.rc",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/trusty/utils/spiproxyd/main.c b/trusty/utils/spiproxyd/main.c
deleted file mode 100644
index c10866b5f..000000000
--- a/trusty/utils/spiproxyd/main.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "spiproxyd"
-
-#include <assert.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <log/log.h>
-#include <stdlib.h>
-#include <string.h>
-#include <trusty/tipc.h>
-#include <unistd.h>
-
-int handle_msg(int trusty_dev_fd, int spi_dev_fd) {
- int rc;
- uint8_t msg_buf[4096];
- size_t msg_len;
-
- /* read request from SPI Trusty app */
- rc = read(trusty_dev_fd, &msg_buf, sizeof(msg_buf));
- if (rc < 0) {
- ALOGE("failed (%d) to read request from TA\n", rc);
- return rc;
- }
- msg_len = rc;
-
- /* forward request to SPI host device */
- rc = write(spi_dev_fd, &msg_buf, msg_len);
- if (rc < 0 || (size_t)rc != msg_len) {
- ALOGE("failed (%d) to forward request to host\n", rc);
- return rc < 0 ? rc : -1;
- }
-
- /* read response from SPI host device */
- rc = read(spi_dev_fd, &msg_buf, sizeof(msg_buf));
- if (rc < 0) {
- ALOGE("failed (%d) to read response from host\n", rc);
- return rc;
- }
- msg_len = rc;
-
- /* forward response to SPI Trusty app */
- rc = write(trusty_dev_fd, &msg_buf, msg_len);
- if (rc < 0 || (size_t)rc != msg_len) {
- ALOGE("failed (%d) to forward response to TA\n", rc);
- return rc < 0 ? rc : -1;
- }
-
- return 0;
-}
-
-int event_loop(int trusty_dev_fd, int spi_dev_fd) {
- while (true) {
- int rc = handle_msg(trusty_dev_fd, spi_dev_fd);
- if (rc < 0) {
- ALOGE("exiting event loop\n");
- return EXIT_FAILURE;
- }
- }
-}
-
-static void show_usage() {
- ALOGE("usage: spiproxyd -t TRUSTY_DEVICE -s SPI_DEVICE -p SPI_PROXY_PORT\n");
-}
-
-static void parse_args(int argc, char* argv[], const char** trusty_dev_name,
- const char** spi_dev_name, const char** spi_proxy_port) {
- int opt;
- while ((opt = getopt(argc, argv, "ht:s:p:")) != -1) {
- switch (opt) {
- case 'h':
- show_usage();
- exit(EXIT_SUCCESS);
- break;
- case 't':
- *trusty_dev_name = strdup(optarg);
- break;
- case 's':
- *spi_dev_name = strdup(optarg);
- break;
- case 'p':
- *spi_proxy_port = strdup(optarg);
- break;
- default:
- show_usage();
- exit(EXIT_FAILURE);
- break;
- }
- }
-
- if (!*trusty_dev_name || !*spi_dev_name || !*spi_proxy_port) {
- show_usage();
- exit(EXIT_FAILURE);
- }
-}
-
-int main(int argc, char* argv[]) {
- int rc;
- const char* trusty_dev_name = NULL;
- const char* spi_dev_name = NULL;
- const char* spi_proxy_port = NULL;
- int trusty_dev_fd;
- int spi_dev_fd;
-
- parse_args(argc, argv, &trusty_dev_name, &spi_dev_name, &spi_proxy_port);
-
- rc = tipc_connect(trusty_dev_name, spi_proxy_port);
- if (rc < 0) {
- ALOGE("failed (%d) to connect to SPI proxy port\n", rc);
- return rc;
- }
- trusty_dev_fd = rc;
-
- rc = open(spi_dev_name, O_RDWR, 0);
- if (rc < 0) {
- ALOGE("failed (%d) to open SPI device\n", rc);
- return rc;
- }
- spi_dev_fd = rc;
-
- return event_loop(trusty_dev_fd, spi_dev_fd);
-}
diff --git a/trusty/utils/spiproxyd/proxy.rc b/trusty/utils/spiproxyd/proxy.rc
deleted file mode 100644
index 7d63e6ab4..000000000
--- a/trusty/utils/spiproxyd/proxy.rc
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2020 The Android Open-Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-service spiproxyd /vendor/bin/spiproxyd -t /dev/trusty-ipc-dev0 \
- -s /dev/vport3p2 -p com.android.trusty.spi.proxy
- class main
- user system
- group system
- oneshot
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
index 6fc2a48d7..9c8af7b3b 100644
--- a/trusty/utils/trusty-ut-ctrl/Android.bp
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -12,11 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
+cc_test {
name: "trusty-ut-ctrl",
vendor: true,
@@ -28,6 +24,7 @@ cc_binary {
static_libs: [
"libtrusty",
],
+ gtest: false,
cflags: [
"-Wall",
"-Werror",
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 22d171de4..6a339a109 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "usbd",
init_rc: ["usbd.rc"],
diff --git a/watchdogd/Android.bp b/watchdogd/Android.bp
index 03882082f..0fbc33cd2 100644
--- a/watchdogd/Android.bp
+++ b/watchdogd/Android.bp
@@ -1,7 +1,3 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_binary {
name: "watchdogd",
recovery_available: true,