diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index 2449e38bed2b..f222624dfd63 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -264,11 +264,10 @@ if (is_win) { ] } if (is_posix) { - _native_compiler_configs += [ "//build/config/gcc:relative_paths" ] - if (is_product) { - _native_compiler_configs += - [ "//build/config/gcc:symbol_visibility_hidden" ] - } + _native_compiler_configs += [ + "//build/config/gcc:relative_paths", + "//build/config/gcc:symbol_visibility_hidden", + ] } if (is_fuchsia) { _native_compiler_configs += [ diff --git a/build/toolchain/fuchsia/BUILD.gn b/build/toolchain/fuchsia/BUILD.gn index 68f834deee01..f817da43a5dc 100644 --- a/build/toolchain/fuchsia/BUILD.gn +++ b/build/toolchain/fuchsia/BUILD.gn @@ -137,18 +137,21 @@ toolchain("fuchsia") { exename = "{{target_output_name}}{{output_extension}}" outfile = "{{root_out_dir}}/$exename" rspfile = "$outfile.rsp" + symfile = "$outfile.sym" + symbolizer_script = rebase_path("//runtime/tools/dart_profiler_symbols.py") # Note that the unstripped_outfile is in the exe.stripped folder. # We should probably clean this up, but changing this and dart.cmx # to point ./dart instead of ./exe.stripped/dart makes the build # fail because ./dart does not end up in the manifest file. unstripped_outfile = "{{root_out_dir}}/exe.stripped/$exename" - command = "$compiler_prefix $ld $target_triple_flags $sysroot_flags $lto_flags {{ldflags}} -o $unstripped_outfile -Wl,--build-id -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}} && ${strip} -o $outfile $unstripped_outfile" + command = "$compiler_prefix $ld $target_triple_flags $sysroot_flags $lto_flags {{ldflags}} -o $unstripped_outfile -Wl,--build-id -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}} && ${strip} -o $outfile $unstripped_outfile && $symbolizer_script --nm $nm --output $symfile --binary $unstripped_outfile" description = "LINK $outfile" rspfile_content = "{{inputs}}" outputs = [ unstripped_outfile, outfile, + symfile, ] } diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni index 0d749661bda6..2ff9c612de3a 100644 --- a/build/toolchain/gcc_toolchain.gni +++ b/build/toolchain/gcc_toolchain.gni @@ -191,12 +191,20 @@ template("gcc_toolchain") { exename = "{{target_output_name}}{{output_extension}}" outfile = "{{root_out_dir}}/$exename" rspfile = "$outfile.rsp" + symfile = "$outfile.sym" if (defined(invoker.strip) || defined(invoker.llvm_objcopy)) { stripped_outfile = "{{root_out_dir}}/exe.stripped/$exename" } command = "$ld {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group $libs_section_prefix {{libs}} $libs_section_postfix" + + symbolizer_script = + rebase_path("//runtime/tools/dart_profiler_symbols.py") + symbolize_command = + "$symbolizer_script --nm $nm --output $symfile --binary $outfile" + command += " && $symbolize_command" + if (defined(invoker.strip)) { strip = invoker.strip strip_command = @@ -212,7 +220,10 @@ template("gcc_toolchain") { } description = "LINK $outfile" rspfile_content = "{{inputs}}" - outputs = [ outfile ] + outputs = [ + outfile, + symfile, + ] if (defined(invoker.strip) || defined(invoker.llvm_objcopy)) { outputs += [ stripped_outfile ] } diff --git a/build/toolchain/mac/BUILD.gn b/build/toolchain/mac/BUILD.gn index 856812021a77..c3ba7bed5fa5 100644 --- a/build/toolchain/mac/BUILD.gn +++ b/build/toolchain/mac/BUILD.gn @@ -49,6 +49,7 @@ template("mac_toolchain") { cxx = invoker.cxx ar = invoker.ar ld = invoker.ld + nm = invoker.nm # Make these apply to all tools below. lib_switch = "-l" @@ -175,12 +176,20 @@ template("mac_toolchain") { exename = "{{target_output_name}}{{output_extension}}" outfile = "{{root_out_dir}}/$exename" rspfile = "$outfile.rsp" + symfile = "$outfile.sym" if (defined(invoker.strip)) { stripped_outfile = "{{root_out_dir}}/exe.stripped/$exename" } command = "$ld $sysroot_flags $toolchain_flags {{ldflags}} -Xlinker -rpath -Xlinker @executable_path/Frameworks -o $outfile -Wl,-filelist,$rspfile {{solibs}} {{libs}} {{frameworks}}" + + symbolizer_script = + rebase_path("//runtime/tools/dart_profiler_symbols.py") + symbolize_command = + "$symbolizer_script --nm $nm --output $symfile --binary $outfile" + command += " && $symbolize_command" + if (defined(invoker.strip)) { strip = invoker.strip strip_command = "${strip} -x -o $stripped_outfile $outfile" @@ -189,7 +198,10 @@ template("mac_toolchain") { description = "LINK $outfile" rspfile_content = "{{inputs_newline}}" - outputs = [ outfile ] + outputs = [ + outfile, + symfile, + ] if (defined(invoker.strip)) { outputs += [ stripped_outfile ] } @@ -229,6 +241,7 @@ mac_toolchain("clang_x64") { ar = "${prefix}/llvm-ar" ld = cxx strip = "${prefix}/llvm-strip" + nm = "${prefix}/llvm-nm" is_clang = true if (mac_enable_relative_sdk_path) { mac_sdk_path = rebase_path(mac_sdk_path, root_build_dir) @@ -245,6 +258,7 @@ mac_toolchain("clang_arm64") { ar = "${prefix}/llvm-ar" ld = cxx strip = "${prefix}/llvm-strip" + nm = "${prefix}/llvm-nm" is_clang = true if (mac_enable_relative_sdk_path) { mac_sdk_path = rebase_path(mac_sdk_path, root_build_dir) diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index eac51ec0247b..91ebf8feb65c 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -204,11 +204,18 @@ template("build_gen_snapshot") { ] } - if (!is_win) { - # Adds all symbols to the dynamic symbol table, not just used ones. - # This is needed to make native extensions work. It is also needed to get - # symbols in VM-generated backtraces and profiles. - ldflags = [ "-rdynamic" ] + if (is_mac) { + ldflags = [ + "-Wl,-exported_symbol", + "-Wl,_Dart_*", + ] + } else if (!is_win) { + if (!is_clang) { + # TODO(rmacnak): Remove once bots are updated to a newer gcc. + ldflags = [ "-rdynamic" ] + } else { + ldflags = [ "-Wl,--export-dynamic-symbol=Dart_*" ] + } } if (is_win) { @@ -793,11 +800,18 @@ template("dart_executable") { if (is_win) { ldflags = [ "/EXPORT:Dart_True" ] + } else if (is_mac) { + ldflags = [ + "-Wl,-exported_symbol", + "-Wl,_Dart_*", + ] } else { - # Adds all symbols to the dynamic symbol table, not just used ones. - # This is needed to make native extensions work. It is also needed to get - # symbols in VM-generated backtraces and profiles. - ldflags = [ "-rdynamic" ] + if (!is_clang) { + # TODO(rmacnak): Remove once bots are updated to a newer gcc. + ldflags = [ "-rdynamic" ] + } else { + ldflags = [ "-Wl,--export-dynamic-symbol=Dart_*" ] + } } ldflags += extra_ldflags @@ -1018,11 +1032,18 @@ executable("run_vm_tests") { ] + builtin_impl_tests + vm_tests + compiler_tests + heap_tests + io_impl_tests - if (!is_win) { - # Adds all symbols to the dynamic symbol table, not just used ones. - # This is needed to make native extensions work. It is also needed to get - # symbols in VM-generated backtraces and profiles. - ldflags = [ "-rdynamic" ] + if (is_mac) { + ldflags = [ + "-Wl,-exported_symbol", + "-Wl,_Dart_*", + ] + } else if (!is_win) { + if (!is_clang) { + # TODO(rmacnak): Remove once bots are updated to a newer gcc. + ldflags = [ "-rdynamic" ] + } else { + ldflags = [ "-Wl,--export-dynamic-symbol=Dart_*" ] + } } if (is_win) { diff --git a/runtime/bin/exe_utils.cc b/runtime/bin/exe_utils.cc index 7f1b5f1b980e..88e766f94926 100644 --- a/runtime/bin/exe_utils.cc +++ b/runtime/bin/exe_utils.cc @@ -121,5 +121,24 @@ Utils::CStringUniquePtr EXEUtils::GetDirectoryPrefixFromExeName() { : result); } +#if !defined(DART_HOST_OS_WINDOWS) +void EXEUtils::LoadDartProfilerSymbols(const char* exepath) { + int len = strlen(exepath); + char* sympath = reinterpret_cast(malloc(len + 5)); + memcpy(sympath, exepath, len); // NOLINT + memcpy(sympath + len, ".sym", 5); // NOLINT + File* file = File::Open(nullptr, sympath, File::kRead); + free(sympath); + if (file != nullptr) { + int64_t size = file->Length(); + MappedMemory* mapping = file->Map(File::kReadOnly, 0, size); + Dart_AddSymbols(exepath, mapping->address(), size); + mapping->Leak(); // Let us delete the object but keep the mapping. + delete mapping; + file->Release(); + } +} +#endif // !defined(DART_HOST_OS_WINDOWS) + } // namespace bin } // namespace dart diff --git a/runtime/bin/exe_utils.h b/runtime/bin/exe_utils.h index 0c3eafbf3c67..e7ab8b6e5cae 100644 --- a/runtime/bin/exe_utils.h +++ b/runtime/bin/exe_utils.h @@ -20,6 +20,12 @@ class EXEUtils { // Returns the path to the directory the current executable resides in. static Utils::CStringUniquePtr GetDirectoryPrefixFromExeName(); +#if !defined(DART_HOST_OS_WINDOWS) + // Loads a compact symbolization table from "$exepath.sym" that is used by the + // VM's profiler and crash stack trace dumper to symbolize C frames. + static void LoadDartProfilerSymbols(const char* exepath); +#endif + private: DISALLOW_COPY_AND_ASSIGN(EXEUtils); }; diff --git a/runtime/bin/file.h b/runtime/bin/file.h index f883f904dc71..6c217a8ac5f5 100644 --- a/runtime/bin/file.h +++ b/runtime/bin/file.h @@ -35,6 +35,8 @@ class MappedMemory { intptr_t size() const { return size_; } uword start() const { return reinterpret_cast(address()); } + void Leak() { should_unmap_ = false; } + private: void Unmap(); diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc index b87c246e8b36..f76c2eaaf673 100644 --- a/runtime/bin/gen_snapshot.cc +++ b/runtime/bin/gen_snapshot.cc @@ -17,6 +17,7 @@ #include "bin/dartutils.h" #include "bin/error_exit.h" #include "bin/eventhandler.h" +#include "bin/exe_utils.h" #include "bin/file.h" #include "bin/loader.h" #include "bin/options.h" @@ -844,6 +845,11 @@ static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) { } int main(int argc, char** argv) { +#if !defined(DART_HOST_OS_WINDOWS) + // Very early so any crashes during startup can also be symbolized. + EXEUtils::LoadDartProfilerSymbols(argv[0]); +#endif + const int EXTRA_VM_ARGUMENTS = 7; CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS); CommandLineOptions inputs(argc); diff --git a/runtime/bin/main_impl.cc b/runtime/bin/main_impl.cc index 9197663caa91..47836fbb94e7 100644 --- a/runtime/bin/main_impl.cc +++ b/runtime/bin/main_impl.cc @@ -19,6 +19,7 @@ #include "bin/dfe.h" #include "bin/error_exit.h" #include "bin/eventhandler.h" +#include "bin/exe_utils.h" #include "bin/file.h" #include "bin/gzip.h" #include "bin/isolate_data.h" @@ -1152,6 +1153,11 @@ static Dart_GetVMServiceAssetsArchive GetVMServiceAssetsArchiveCallback = #endif // !defined(PRODUCT) void main(int argc, char** argv) { +#if !defined(DART_HOST_OS_WINDOWS) + // Very early so any crashes during startup can also be symbolized. + EXEUtils::LoadDartProfilerSymbols(argv[0]); +#endif + char* script_name = nullptr; // Allows the dartdev process to point to the desired package_config. char* package_config_override = nullptr; diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc index 20f31f6faffb..b80e116bd580 100644 --- a/runtime/bin/run_vm_tests.cc +++ b/runtime/bin/run_vm_tests.cc @@ -7,6 +7,7 @@ #include "bin/dartutils.h" #include "bin/dfe.h" #include "bin/eventhandler.h" +#include "bin/exe_utils.h" #include "bin/file.h" #include "bin/loader.h" #include "bin/platform.h" @@ -293,6 +294,11 @@ void ShiftArgs(int* argc, const char** argv) { } static int Main(int argc, const char** argv) { +#if !defined(DART_HOST_OS_WINDOWS) + // Very early so any crashes during startup can also be symbolized. + bin::EXEUtils::LoadDartProfilerSymbols(argv[0]); +#endif + // Flags being passed to the Dart VM. int dart_argc = 0; const char** dart_argv = nullptr; diff --git a/runtime/tests/vm/dart/exported_symbols_test.dart b/runtime/tests/vm/dart/exported_symbols_test.dart new file mode 100644 index 000000000000..d76915fa6713 --- /dev/null +++ b/runtime/tests/vm/dart/exported_symbols_test.dart @@ -0,0 +1,338 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:io"; + +import "package:expect/expect.dart"; + +main() { + if (Platform.isWindows) return; + if (Platform.isAndroid) return; // no nm available on test device + + var result = Process.runSync("nm", [ + Platform.isMacOS ? "--extern-only" : "--dynamic", + "--defined-only", + "--format=posix", + Platform.executable + ]); + if (result.exitCode != 0) { + print("nm failed"); + print(result.stdout); + print(result.stderr); + throw "nm failed"; + } + + var symbols = result.stdout.split("\n")..remove(""); + for (var i = 0; i < symbols.length; i++) { + // Get just the symbol name. Old nm on bots doesn't have --just-symbols. + symbols[i] = symbols[i].split(" ")[0]; + if (Platform.isMacOS) { + // Remove leading underscores. + symbols[i] = symbols[i].substring(1); + } + } + symbols.remove("_IO_stdin_used"); // Only on IA32 for libc. + print(symbols); + + Expect.setEquals(symbols, [ + "Dart_AddSymbols", + "Dart_Allocate", + "Dart_AllocateWithNativeFields", + "Dart_BooleanValue", + "Dart_ClassLibrary", + "Dart_ClassName", + "Dart_Cleanup", + "Dart_CloseNativePort", + "Dart_ClosureFunction", + "Dart_CompileAll", + "Dart_CompileToKernel", + "Dart_CreateAppAOTSnapshotAsAssemblies", + "Dart_CreateAppAOTSnapshotAsAssembly", + "Dart_CreateAppAOTSnapshotAsElf", + "Dart_CreateAppAOTSnapshotAsElfs", + "Dart_CreateAppJITSnapshotAsBlobs", + "Dart_CreateCoreJITSnapshotAsBlobs", + "Dart_CreateIsolateGroup", + "Dart_CreateIsolateGroupFromKernel", + "Dart_CreateIsolateInGroup", + "Dart_CreateSnapshot", + "Dart_CreateVMAOTSnapshotAsAssembly", + "Dart_CurrentIsolate", + "Dart_CurrentIsolateData", + "Dart_CurrentIsolateGroup", + "Dart_CurrentIsolateGroupData", + "Dart_CurrentIsolateGroupId", + "Dart_DebugName", + "Dart_DebugNameToCString", + "Dart_DefaultCanonicalizeUrl", + "Dart_DeferredLoadComplete", + "Dart_DeferredLoadCompleteError", + "Dart_DeleteFinalizableHandle", + "Dart_DeletePersistentHandle", + "Dart_DeleteWeakPersistentHandle", + "Dart_DetectNullSafety", + "Dart_DisableHeapSampling", + "Dart_DoubleValue", + "Dart_DumpNativeStackTrace", + "Dart_EmptyString", + "Dart_EnableHeapSampling", + "Dart_EnterIsolate", + "Dart_EnterScope", + "Dart_ErrorGetException", + "Dart_ErrorGetStackTrace", + "Dart_ErrorHasException", + "Dart_ExecuteInternalCommand", + "Dart_ExitIsolate", + "Dart_ExitScope", + "Dart_False", + "Dart_FinalizeAllClasses", + "Dart_FinalizeLoading", + "Dart_FunctionIsStatic", + "Dart_FunctionName", + "Dart_FunctionOwner", + "Dart_GetClass", + "Dart_GetCurrentUserTag", + "Dart_GetDataFromByteBuffer", + "Dart_GetDefaultUserTag", + "Dart_GetError", + "Dart_GetField", + "Dart_GetLoadedLibraries", + "Dart_GetMainPortId", + "Dart_GetMessageNotifyCallback", + "Dart_GetNativeArgument", + "Dart_GetNativeArgumentCount", + "Dart_GetNativeArguments", + "Dart_GetNativeBooleanArgument", + "Dart_GetNativeDoubleArgument", + "Dart_GetNativeFieldsOfArgument", + "Dart_GetNativeInstanceField", + "Dart_GetNativeInstanceFieldCount", + "Dart_GetNativeIntegerArgument", + "Dart_GetNativeIsolateGroupData", + "Dart_GetNativeReceiver", + "Dart_GetNativeResolver", + "Dart_GetNativeStringArgument", + "Dart_GetNativeSymbol", + "Dart_GetNonNullableType", + "Dart_GetNullableType", + "Dart_GetObfuscationMap", + "Dart_GetPeer", + "Dart_GetStaticMethodClosure", + "Dart_GetStickyError", + "Dart_GetType", + "Dart_GetTypeOfExternalTypedData", + "Dart_GetTypeOfTypedData", + "Dart_GetUserTagLabel", + "Dart_HandleFromPersistent", + "Dart_HandleFromWeakPersistent", + "Dart_HandleMessage", + "Dart_HandleServiceMessages", + "Dart_HasLivePorts", + "Dart_HasServiceMessages", + "Dart_HasStickyError", + "Dart_IdentityEquals", + "Dart_Initialize", + "Dart_InstanceGetType", + "Dart_IntegerFitsIntoInt64", + "Dart_IntegerFitsIntoUint64", + "Dart_IntegerToHexCString", + "Dart_IntegerToInt64", + "Dart_IntegerToUint64", + "Dart_Invoke", + "Dart_InvokeClosure", + "Dart_InvokeConstructor", + "Dart_InvokeVMServiceMethod", + "Dart_IsApiError", + "Dart_IsBoolean", + "Dart_IsByteBuffer", + "Dart_IsClosure", + "Dart_IsCompilationError", + "Dart_IsDouble", + "Dart_IsError", + "Dart_IsExternalString", + "Dart_IsFatalError", + "Dart_IsFunction", + "Dart_IsFuture", + "Dart_IsInstance", + "Dart_IsInteger", + "Dart_IsKernel", + "Dart_IsKernelIsolate", + "Dart_IsLegacyType", + "Dart_IsLibrary", + "Dart_IsList", + "Dart_IsMap", + "Dart_IsNonNullableType", + "Dart_IsNull", + "Dart_IsNullableType", + "Dart_IsNumber", + "Dart_IsolateData", + "Dart_IsolateFlagsInitialize", + "Dart_IsolateGroupData", + "Dart_IsolateGroupHeapNewCapacityMetric", + "Dart_IsolateGroupHeapNewExternalMetric", + "Dart_IsolateGroupHeapNewUsedMetric", + "Dart_IsolateGroupHeapOldCapacityMetric", + "Dart_IsolateGroupHeapOldExternalMetric", + "Dart_IsolateGroupHeapOldUsedMetric", + "Dart_IsolateMakeRunnable", + "Dart_IsolateRunnableHeapSizeMetric", + "Dart_IsolateRunnableLatencyMetric", + "Dart_IsolateServiceId", + "Dart_IsPausedOnExit", + "Dart_IsPausedOnStart", + "Dart_IsPrecompiledRuntime", + "Dart_IsReloading", + "Dart_IsServiceIsolate", + "Dart_IsString", + "Dart_IsStringLatin1", + "Dart_IsTearOff", + "Dart_IsType", + "Dart_IsTypedData", + "Dart_IsTypeVariable", + "Dart_IsUnhandledExceptionError", + "Dart_IsVariable", + "Dart_IsVMFlagSet", + "Dart_KernelIsolateIsRunning", + "Dart_KernelListDependencies", + "Dart_KernelPort", + "Dart_KillIsolate", + "Dart_LibraryHandleError", + "Dart_LibraryResolvedUrl", + "Dart_LibraryUrl", + "Dart_ListGetAsBytes", + "Dart_ListGetAt", + "Dart_ListGetRange", + "Dart_ListLength", + "Dart_ListSetAsBytes", + "Dart_ListSetAt", + "Dart_LoadingUnitLibraryUris", + "Dart_LoadLibrary", + "Dart_LoadLibraryFromKernel", + "Dart_LoadScriptFromKernel", + "Dart_LookupLibrary", + "Dart_MapContainsKey", + "Dart_MapGetAt", + "Dart_MapKeys", + "Dart_New", + "Dart_NewApiError", + "Dart_NewBoolean", + "Dart_NewByteBuffer", + "Dart_NewCompilationError", + "Dart_NewDouble", + "Dart_NewExternalLatin1String", + "Dart_NewExternalTypedData", + "Dart_NewExternalTypedDataWithFinalizer", + "Dart_NewExternalUTF16String", + "Dart_NewFinalizableHandle", + "Dart_NewInteger", + "Dart_NewIntegerFromHexCString", + "Dart_NewIntegerFromUint64", + "Dart_NewList", + "Dart_NewListOf", + "Dart_NewListOfType", + "Dart_NewListOfTypeFilled", + "Dart_NewNativePort", + "Dart_NewPersistentHandle", + "Dart_NewSendPort", + "Dart_NewStringFromCString", + "Dart_NewStringFromUTF16", + "Dart_NewStringFromUTF32", + "Dart_NewStringFromUTF8", + "Dart_NewTypedData", + "Dart_NewUnhandledExceptionError", + "Dart_NewUnmodifiableExternalTypedDataWithFinalizer", + "Dart_NewUserTag", + "Dart_NewWeakPersistentHandle", + "Dart_NotifyDestroyed", + "Dart_NotifyIdle", + "Dart_NotifyLowMemory", + "Dart_Null", + "Dart_ObjectEquals", + "Dart_ObjectIsType", + "Dart_Post", + "Dart_PostCObject", + "Dart_PostInteger", + "Dart_Precompile", + "Dart_PrepareToAbort", + "Dart_PropagateError", + "Dart_RecordTimelineEvent", + "Dart_RegisterHeapSamplingCallback", + "Dart_RegisterIsolateServiceRequestCallback", + "Dart_RegisterRootServiceRequestCallback", + "Dart_ReportSurvivingAllocations", + "Dart_ReThrowException", + "Dart_RootLibrary", + "Dart_RunLoop", + "Dart_RunLoopAsync", + "Dart_ScopeAllocate", + "Dart_SendPortGetId", + "Dart_ServiceSendDataEvent", + "Dart_SetBooleanReturnValue", + "Dart_SetCurrentUserTag", + "Dart_SetDartLibrarySourcesKernel", + "Dart_SetDeferredLoadHandler", + "Dart_SetDoubleReturnValue", + "Dart_SetDwarfStackTraceFootnoteCallback", + "Dart_SetEmbedderInformationCallback", + "Dart_SetEnabledTimelineCategory", + "Dart_SetEnvironmentCallback", + "Dart_SetFfiNativeResolver", + "Dart_SetField", + "Dart_SetFileModifiedCallback", + "Dart_SetHeapSamplingPeriod", + "Dart_SetIntegerReturnValue", + "Dart_SetLibraryTagHandler", + "Dart_SetMessageNotifyCallback", + "Dart_SetNativeInstanceField", + "Dart_SetNativeResolver", + "Dart_SetPausedOnExit", + "Dart_SetPausedOnStart", + "Dart_SetPeer", + "Dart_SetPerformanceMode", + "Dart_SetPersistentHandle", + "Dart_SetReturnValue", + "Dart_SetRootLibrary", + "Dart_SetServiceStreamCallbacks", + "Dart_SetShouldPauseOnExit", + "Dart_SetShouldPauseOnStart", + "Dart_SetStickyError", + "Dart_SetThreadName", + "Dart_SetTimelineRecorderCallback", + "Dart_SetVMFlags", + "Dart_SetWeakHandleReturnValue", + "Dart_ShouldPauseOnExit", + "Dart_ShouldPauseOnStart", + "Dart_ShutdownIsolate", + "Dart_SortClasses", + "Dart_StartProfiling", + "Dart_StopProfiling", + "Dart_StringGetProperties", + "Dart_StringLength", + "Dart_StringStorageSize", + "Dart_StringToCString", + "Dart_StringToLatin1", + "Dart_StringToUTF16", + "Dart_StringToUTF8", + "Dart_ThreadDisableProfiling", + "Dart_ThreadEnableProfiling", + "Dart_ThrowException", + "Dart_TimelineEvent", + "Dart_TimelineGetMicros", + "Dart_TimelineGetTicks", + "Dart_TimelineGetTicksFrequency", + "Dart_ToString", + "Dart_True", + "Dart_TypedDataAcquireData", + "Dart_TypedDataReleaseData", + "Dart_TypeDynamic", + "Dart_TypeNever", + "Dart_TypeToNonNullableType", + "Dart_TypeToNullableType", + "Dart_TypeVoid", + "Dart_VersionString", + "Dart_WaitForEvent", + "Dart_WriteHeapSnapshot", + "Dart_WriteProfileToTimeline", + ]); +} diff --git a/runtime/tests/vm/dart/symbolized_crash_test.dart b/runtime/tests/vm/dart/symbolized_crash_test.dart new file mode 100644 index 000000000000..0544c6cc6309 --- /dev/null +++ b/runtime/tests/vm/dart/symbolized_crash_test.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:io"; + +import 'package:expect/expect.dart'; +import 'package:path/path.dart' as path; + +main() { + if (Platform.isWindows) return; // posix exit codes + if (Platform.isAndroid) return; // run_vm_tests not available on test device + + var run_vm_tests = + path.join(path.dirname(Platform.resolvedExecutable), "run_vm_tests"); + var result = Process.runSync(run_vm_tests, ["Fatal"]); + print(result.exitCode); + print(result.stdout); + print(result.stderr); + + Expect.contains( + "error: This test fails and produces a backtrace", result.stderr); + + // Check for the frames that are marked never inline or have their address + // taken, and so should be stable to changes in the C compiler. There are of + // course more frames. + Expect.contains("dart::Assert::Fail", result.stderr); + Expect.contains("Dart_TestFatal", result.stderr); + Expect.contains("main", result.stderr); +} diff --git a/runtime/tools/dart_profiler_symbols.py b/runtime/tools/dart_profiler_symbols.py new file mode 100755 index 000000000000..d61379872e42 --- /dev/null +++ b/runtime/tools/dart_profiler_symbols.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# On Fuchsia, in lieu of the ELF dynamic symbol table consumed through dladdr, +# the Dart VM profiler consumes symbols produced by this tool, which have the +# format +# +# struct { +# uint32_t num_entries; +# struct { +# uint32_t offset; +# uint32_t size; +# uint32_t string_table_offset; +# } entries[num_entries]; +# const char* string_table; +# } +# +# Entries are sorted by offset. String table entries are NUL-terminated. +# +# See also //third_party/dart/runtime/vm/native_symbol_fuchsia.cc + +import optparse +import os +import re +import utils +import subprocess +import struct + + +class Symbol: + + def __init__(self, offset, size, name): + self.offset = offset + self.size = size + self.name = name + + +parser = optparse.OptionParser() +parser.add_option("--nm", type="string", help="Path to `nm` tool") +parser.add_option("--binary", type="string") +parser.add_option("--output", type="string") +options = parser.parse_args()[0] +nm = options.nm +if not nm: + raise Exception('--nm not specified') +binary = options.binary +if not binary: + raise Exception('--binary not specified') +output = options.output +if not output: + raise Exception('--output not specified') + +p = subprocess.Popen( + [nm, "--demangle", "--numeric-sort", "--print-size", binary], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) +nm_output, _ = p.communicate() +nm_lines = nm_output.decode('utf-8').split('\n') +regex = re.compile("([0-9A-Za-z]+) ([0-9A-Za-z]+) (t|T|w|W) (.*)") +symbols = [] +for line in nm_lines: + m = regex.match(line) + if not m: + continue + offset = int(m.group(1), 16) + if offset > 0x100000000: + # Mac adds an extra 4GB for some reason + offset -= 0x100000000 + size = int(m.group(2), 16) + name = m.group(4).split("(")[0] + if name == "__mh_execute_header": + # Skip very out-of-range thing. + continue + symbols.append(Symbol(offset, size, name.encode('utf-8'))) + +if len(symbols) == 0: + raise Exception(binary + " has no symbols") + +stream = open(output, "wb") +stream.write(struct.pack("I", len(symbols))) +nameOffset = 0 +for symbol in symbols: + stream.write(struct.pack("I", symbol.offset)) + stream.write(struct.pack("I", symbol.size)) + stream.write(struct.pack("I", nameOffset)) + nameOffset += len(symbol.name) + nameOffset += 1 +for symbol in symbols: + stream.write(symbol.name) + stream.write(b"\0") +stream.close() diff --git a/runtime/vm/assert_test.cc b/runtime/vm/assert_test.cc index 4fe217c799f8..f4d22cfc2571 100644 --- a/runtime/vm/assert_test.cc +++ b/runtime/vm/assert_test.cc @@ -54,3 +54,7 @@ VM_UNIT_TEST_CASE(Fail1) { VM_UNIT_TEST_CASE(Fail2) { FAIL("This test fails with two arguments: %d, %d", -100, 42); } + +VM_UNIT_TEST_CASE_WITH_EXPECTATION(Fatal, "Crash") { + FATAL("This test fails and produces a backtrace"); +} diff --git a/runtime/vm/native_symbol_android.cc b/runtime/vm/native_symbol_android.cc deleted file mode 100644 index faff281f99e0..000000000000 --- a/runtime/vm/native_symbol_android.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "platform/globals.h" -#if defined(DART_HOST_OS_ANDROID) - -#include "vm/native_symbol.h" -#include "vm/os.h" - -#include // NOLINT -#include // NOLINT - -namespace dart { - -void NativeSymbolResolver::Init() {} - -void NativeSymbolResolver::Cleanup() {} - -char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) { - Dl_info info; - int r = dladdr(reinterpret_cast(pc), &info); - if (r == 0) { - return nullptr; - } - if (info.dli_sname == nullptr) { - return nullptr; - } - if (start != nullptr) { - *start = reinterpret_cast(info.dli_saddr); - } - int status = 0; - size_t len = 0; - char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, &len, &status); - MSAN_UNPOISON(demangled, len); - if (status == 0) { - return demangled; - } - return strdup(info.dli_sname); -} - -void NativeSymbolResolver::FreeSymbolName(char* name) { - free(name); -} - -bool NativeSymbolResolver::LookupSharedObject(uword pc, - uword* dso_base, - char** dso_name) { - Dl_info info; - int r = dladdr(reinterpret_cast(pc), &info); - if (r == 0) { - return false; - } - if (dso_base != nullptr) { - *dso_base = reinterpret_cast(info.dli_fbase); - } - if (dso_name != nullptr) { - *dso_name = strdup(info.dli_fname); - } - return true; -} - -void NativeSymbolResolver::AddSymbols(const char* dso_name, - void* buffer, - size_t size) { - OS::PrintErr("warning: Dart_AddSymbols has no effect on Android\n"); -} - -} // namespace dart - -#endif // defined(DART_HOST_OS_ANDROID) diff --git a/runtime/vm/native_symbol_linux.cc b/runtime/vm/native_symbol_linux.cc deleted file mode 100644 index 2fe493852718..000000000000 --- a/runtime/vm/native_symbol_linux.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "vm/globals.h" -#if defined(DART_HOST_OS_LINUX) - -#include "platform/memory_sanitizer.h" -#include "vm/native_symbol.h" -#include "vm/os.h" - -#include // NOLINT -#include // NOLINT - -namespace dart { - -void NativeSymbolResolver::Init() {} - -void NativeSymbolResolver::Cleanup() {} - -char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) { - Dl_info info; - int r = dladdr(reinterpret_cast(pc), &info); - if (r == 0) { - return nullptr; - } - if (info.dli_sname == nullptr) { - return nullptr; - } - if (start != nullptr) { - *start = reinterpret_cast(info.dli_saddr); - } - int status = 0; - size_t len = 0; - char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, &len, &status); - MSAN_UNPOISON(demangled, len); - if (status == 0) { - return demangled; - } - return strdup(info.dli_sname); -} - -void NativeSymbolResolver::FreeSymbolName(char* name) { - free(name); -} - -bool NativeSymbolResolver::LookupSharedObject(uword pc, - uword* dso_base, - char** dso_name) { - Dl_info info; - int r = dladdr(reinterpret_cast(pc), &info); - if (r == 0) { - return false; - } - if (dso_base != nullptr) { - *dso_base = reinterpret_cast(info.dli_fbase); - } - if (dso_name != nullptr) { - *dso_name = strdup(info.dli_fname); - } - return true; -} - -void NativeSymbolResolver::AddSymbols(const char* dso_name, - void* buffer, - size_t size) { - OS::PrintErr("warning: Dart_AddSymbols has no effect on Linux\n"); -} - -} // namespace dart - -#endif // defined(DART_HOST_OS_LINUX) diff --git a/runtime/vm/native_symbol_macos.cc b/runtime/vm/native_symbol_macos.cc deleted file mode 100644 index 89593ec141e6..000000000000 --- a/runtime/vm/native_symbol_macos.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "vm/globals.h" -#if defined(DART_HOST_OS_MACOS) - -#include "vm/native_symbol.h" -#include "vm/os.h" - -#include // NOLINT -#include // NOLINT - -namespace dart { - -void NativeSymbolResolver::Init() {} - -void NativeSymbolResolver::Cleanup() {} - -char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) { - Dl_info info; - int r = dladdr(reinterpret_cast(pc), &info); - if (r == 0) { - return nullptr; - } - if (info.dli_sname == nullptr) { - return nullptr; - } - if (start != nullptr) { - *start = reinterpret_cast(info.dli_saddr); - } - int status; - char* demangled = - abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); - if (status == 0) { - return demangled; - } - return strdup(info.dli_sname); -} - -void NativeSymbolResolver::FreeSymbolName(char* name) { - free(name); -} - -bool NativeSymbolResolver::LookupSharedObject(uword pc, - uword* dso_base, - char** dso_name) { - Dl_info info; - int r = dladdr(reinterpret_cast(pc), &info); - if (r == 0) { - return false; - } - if (dso_base != nullptr) { - *dso_base = reinterpret_cast(info.dli_fbase); - } - if (dso_name != nullptr) { - *dso_name = strdup(info.dli_fname); - } - return true; -} - -void NativeSymbolResolver::AddSymbols(const char* dso_name, - void* buffer, - size_t size) { - OS::PrintErr("warning: Dart_AddSymbols has no effect on MacOS\n"); -} - -} // namespace dart - -#endif // defined(DART_HOST_OS_MACOS) diff --git a/runtime/vm/native_symbol_fuchsia.cc b/runtime/vm/native_symbol_posix.cc similarity index 80% rename from runtime/vm/native_symbol_fuchsia.cc rename to runtime/vm/native_symbol_posix.cc index 021c2a6f00b8..458ecbc2929c 100644 --- a/runtime/vm/native_symbol_fuchsia.cc +++ b/runtime/vm/native_symbol_posix.cc @@ -1,13 +1,15 @@ -// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/globals.h" -#if defined(DART_HOST_OS_FUCHSIA) +#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \ + defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) #include "platform/memory_sanitizer.h" #include "vm/native_symbol.h" +#include // NOLINT #include // NOLINT namespace dart { @@ -124,7 +126,26 @@ char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) { } } +#if !defined(DART_HOST_OS_FUCHSIA) + // Fallback for libc, etc. + if (info.dli_sname == nullptr) { + return nullptr; + } + if (start != nullptr) { + *start = reinterpret_cast(info.dli_saddr); + } + int status = 0; + size_t len = 0; + char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, &len, &status); + MSAN_UNPOISON(demangled, len); + if (status == 0) { + return demangled; + } + return strdup(info.dli_sname); +#else + // Never works on Fuchsia; avoid linking in cxa_demangle. return nullptr; +#endif } void NativeSymbolResolver::FreeSymbolName(char* name) { @@ -149,7 +170,8 @@ bool NativeSymbolResolver::LookupSharedObject(uword pc, } void NativeSymbolResolver::AddSymbols(const char* dso_name, - void* buffer, size_t size) { + void* buffer, + size_t size) { NativeSymbols* symbols = new NativeSymbols(dso_name, buffer, size); symbols->set_next(symbols_); symbols_ = symbols; @@ -157,4 +179,5 @@ void NativeSymbolResolver::AddSymbols(const char* dso_name, } // namespace dart -#endif // defined(DART_HOST_OS_FUCHSIA) +#endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \ + // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc index e3dd160693ec..42d3ea694277 100644 --- a/runtime/vm/profiler.cc +++ b/runtime/vm/profiler.cc @@ -71,7 +71,12 @@ ProfilerCounters Profiler::counters_ = {}; static void DumpStackFrame(intptr_t frame_index, uword pc, uword fp) { uword start = 0; - if (auto const name = NativeSymbolResolver::LookupSymbolName(pc, &start)) { + // The pc for all frames except the top frame is a return address, which can + // belong to a different inlining interval than the call. Subtract one to get + // the symbolization for the call. + uword lookup_pc = frame_index == 0 ? pc : pc - 1; + if (auto const name = + NativeSymbolResolver::LookupSymbolName(lookup_pc, &start)) { uword offset = pc - start; OS::PrintErr(" pc 0x%" Pp " fp 0x%" Pp " %s+0x%" Px "\n", pc, fp, name, offset); diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index a7c5f3ba272e..b893dcb7f68a 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni @@ -179,10 +179,7 @@ vm_sources = [ "native_message_handler.cc", "native_message_handler.h", "native_symbol.h", - "native_symbol_android.cc", - "native_symbol_fuchsia.cc", - "native_symbol_linux.cc", - "native_symbol_macos.cc", + "native_symbol_posix.cc", "native_symbol_win.cc", "object.cc", "object.h", diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index f0a658620a54..ca36cf5a8bf2 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -261,6 +261,8 @@ copy_trees("copy_trees") { sources = copy_tree_specs } +_has_dot_sym = !is_win && rebase_path(".") == rebase_path("//sdk") + # Copies the Dart VM binary into bin/ if (target_os != current_os && target_os == "fuchsia") { # In the Fuchsia build, this has to use a symlink for two reasons. @@ -298,6 +300,9 @@ if (target_os != current_os && target_os == "fuchsia") { if (is_win && dart_lib_export_symbols) { sources += [ "$dart_out/dart.lib" ] } + if (_has_dot_sym) { + sources += [ "$dart_out/dart.sym" ] + } outputs = [ "$root_out_dir/$dart_sdk_output/bin/{{source_file_part}}" ] } } @@ -315,8 +320,15 @@ copy("copy_dartaotruntime") { ] } -copy("copy_gen_snapshot") { +group("copy_gen_snapshot") { visibility = [ ":group_dart2native" ] + public_deps = [ ":copy_gen_snapshot_exe" ] + if (_has_dot_sym) { + public_deps += [ ":copy_gen_snapshot_sym" ] + } +} + +copy("copy_gen_snapshot_exe") { deps = [ "../runtime/bin:gen_snapshot_product" ] src_dir = get_label_info("../runtime/bin:gen_snapshot_product", "root_out_dir") @@ -326,6 +338,14 @@ copy("copy_gen_snapshot") { ] } +if (_has_dot_sym) { + copy("copy_gen_snapshot_sym") { + deps = [ "../runtime/bin:gen_snapshot_product" ] + sources = [ "$root_out_dir/gen_snapshot_product.sym" ] + outputs = [ "$root_out_dir/$dart_sdk_output/bin/utils/gen_snapshot${executable_suffix}.sym" ] + } +} + copy("copy_vm_platform_strong_product") { visibility = [ ":group_dart2native" ] deps = [ "../runtime/vm:vm_platform_product" ]