Make REACTOR_EMIT_DEBUG_INFO work on Windows and add "print on emit" support

- Use boost::stacktrace instead of libbacktrace so that we get can get
  callstacks on Windows as well. Note that on non-Windows, we tell
  boost::stacktrace to use libbacktrace.

- REACTOR_EMIT_DEBUG_INFO can now be enabled on all platforms

- Fix RR_LOG not building on MSVC by using __FUNCSIG__ instead of
  __PRETTY_FUNCTION__.

- Enable CMake option REACTOR_EMIT_PRINT_LOCATION so that EmitLocation
  in LLVMReactorDebugInfo.cpp injects printfs with func/file/line.
  Sample output:
    rr> sw::Blitter::generate [c:\src\swiftshader\src\renderer\blitter.cpp:1303]
    rr> sw::Blitter::generate [c:\src\swiftshader\src\renderer\blitter.cpp:1304]
    rr> sw::Blitter::ComputeOffset [c:\src\swiftshader\src\renderer\blitter.cpp:1148]
    rr> sw::Blitter::generate [c:\src\swiftshader\src\renderer\blitter.cpp:1312]
    rr> sw::Blitter::generate [c:\src\swiftshader\src\renderer\blitter.cpp:1314]
    rr> sw::SetupRoutine::edge [c:\src\swiftshader\src\shader\setuproutine.cpp:566]

Bug: b/131425026
Change-Id: Ia3b78652f4a76dcd6597ce2df61a236c4a90acc2
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30030
Tested-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2b4c0a1..b270abe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,13 +75,14 @@
 option(BUILD_SAMPLES "Build sample programs" 1)
 option(BUILD_TESTS "Build test programs" 1)
 
-option (MSAN "Build with memory sanitizer" 0)
-option (ASAN "Build with address sanitizer" 0)
-option (TSAN "Build with thread sanitizer" 0)
-option (UBSAN "Build with undefined behavior sanitizer" 0)
-option (WARNINGS_AS_ERRORS "Treat all warnings as errors" 1)
-option (DCHECK_ALWAYS_ON "Check validation macros even in release builds" 0)
-option (REACTOR_EMIT_DEBUG_INFO "Emit debug info for JIT functions" 0)
+option(MSAN "Build with memory sanitizer" 0)
+option(ASAN "Build with address sanitizer" 0)
+option(TSAN "Build with thread sanitizer" 0)
+option(UBSAN "Build with undefined behavior sanitizer" 0)
+option(WARNINGS_AS_ERRORS "Treat all warnings as errors" 1)
+option(DCHECK_ALWAYS_ON "Check validation macros even in release builds" 0)
+option(REACTOR_EMIT_DEBUG_INFO "Emit debug info for JIT functions" 0)
+option(REACTOR_EMIT_PRINT_LOCATION "Emit printing of location info for JIT functions" 0)
 
 if(ARCH STREQUAL "arm")
     set(DEFAULT_REACTOR_BACKEND "Subzero")
@@ -336,10 +337,6 @@
         list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DDCHECK_ALWAYS_ON")
     endif()
 
-    if(REACTOR_EMIT_DEBUG_INFO)
-        list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_DEBUG_INFO")
-    endif()
-
     # Disable pedanitc warnings
     if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
         list(APPEND SWIFTSHADER_COMPILE_OPTIONS
@@ -383,6 +380,17 @@
     endif()
 endif()
 
+if(REACTOR_EMIT_PRINT_LOCATION)
+    # This feature depends on REACTOR_EMIT_DEBUG_INFO, so enable it
+    set(REACTOR_EMIT_DEBUG_INFO "On")
+    list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_EMIT_PRINT_LOCATION")
+endif()
+
+if(REACTOR_EMIT_DEBUG_INFO)
+    message(WARNING "REACTOR_EMIT_DEBUG_INFO is enabled. This will likely affect performance.")
+    list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-DENABLE_RR_DEBUG_INFO")
+endif()
+
 if(WIN32)
     add_definitions(-DWINVER=0x501 -DNOMINMAX -DSTRICT)
     set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "" "lib")
@@ -1232,7 +1240,6 @@
 
 if(WIN32)
     list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/windows/include)
-    list(APPEND LIBBACKTRACE_INCLUDE_DIR ${LIBBACKTRACE_CONFIG_DIR}/windows/include)
 elseif(LINUX)
     list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/linux/include)
     list(APPEND LIBBACKTRACE_INCLUDE_DIR ${LIBBACKTRACE_CONFIG_DIR}/linux/include)
@@ -1375,6 +1382,7 @@
 
     set(SUBZERO_REACTOR_LIST
         ${SOURCE_DIR}/Reactor/Reactor.cpp
+        ${SOURCE_DIR}/Reactor/Reactor.hpp
         ${SOURCE_DIR}/Reactor/SubzeroReactor.cpp
         ${SOURCE_DIR}/Reactor/Routine.cpp
         ${SOURCE_DIR}/Reactor/Optimizer.cpp
@@ -1505,6 +1513,7 @@
 
 set(REACTOR_LLVM_LIST
     ${SOURCE_DIR}/Reactor/Reactor.cpp
+    ${SOURCE_DIR}/Reactor/Reactor.hpp
     ${SOURCE_DIR}/Reactor/LLVMReactor.cpp
     ${SOURCE_DIR}/Reactor/LLVMReactor.hpp
     ${SOURCE_DIR}/Reactor/LLVMReactorDebugInfo.cpp
@@ -1708,14 +1717,31 @@
     target_link_libraries(ReactorLLVM llvm ${OS_LIBS})
 
     if(REACTOR_EMIT_DEBUG_INFO)
-        add_library(Libbacktrace STATIC ${LIBBACKTRACE_LIST})
-        set_target_properties(Libbacktrace PROPERTIES
-            INCLUDE_DIRECTORIES "${LIBBACKTRACE_INCLUDE_DIR}"
-            POSITION_INDEPENDENT_CODE 1
-            FOLDER "Core"
+        add_library(Boost INTERFACE)
+        target_include_directories(Boost INTERFACE
+            "${CMAKE_CURRENT_SOURCE_DIR}/third_party/boost"
         )
-        target_link_libraries(Libbacktrace ${OS_LIBS})
-        target_link_libraries(ReactorLLVM Libbacktrace)
+        if(WIN32)
+            # Boost stacktrace uses COM on Windows.
+            # On Windows, cache COM instances in TLS for performance.
+            target_compile_definitions(Boost INTERFACE BOOST_STACKTRACE_USE_WINDBG_CACHED)
+        else()
+            # Boost stacktrace uses libbacktrace
+            target_compile_definitions(Boost INTERFACE BOOST_STACKTRACE_USE_BACKTRACE)
+
+            # Boost stacktrace uses exceptions, so force enable it
+            set_cpp_flag("-fexceptions")
+
+            add_library(Libbacktrace STATIC ${LIBBACKTRACE_LIST})
+            set_target_properties(Libbacktrace PROPERTIES
+                INCLUDE_DIRECTORIES "${LIBBACKTRACE_INCLUDE_DIR}"
+                POSITION_INDEPENDENT_CODE 1
+                FOLDER "Core"
+            )
+            target_link_libraries(Libbacktrace ${OS_LIBS})
+            target_link_libraries(ReactorLLVM Libbacktrace)
+        endif()
+        target_link_libraries(ReactorLLVM Boost)
     endif(REACTOR_EMIT_DEBUG_INFO)
 
     set(Reactor ReactorLLVM)
diff --git a/src/Reactor/LLVMReactorDebugInfo.cpp b/src/Reactor/LLVMReactorDebugInfo.cpp
index 5b00585..d021a5f 100644
--- a/src/Reactor/LLVMReactorDebugInfo.cpp
+++ b/src/Reactor/LLVMReactorDebugInfo.cpp
@@ -19,7 +19,7 @@
 #include "Reactor.hpp"
 #include "LLVMReactor.hpp"
 
-#include "backtrace.h"
+#include "boost/stacktrace.hpp"
 
 #include "llvm/Demangle/Demangle.h"
 #include "llvm/ExecutionEngine/JITEventListener.h"
@@ -109,6 +109,20 @@
 		auto const& backtrace = getCallerBacktrace();
 		syncScope(backtrace);
 		builder->SetCurrentDebugLocation(getLocation(backtrace, backtrace.size() - 1));
+
+#ifdef ENABLE_RR_EMIT_PRINT_LOCATION
+		static Location lastLocation;
+		if (backtrace.size() == 0)
+		{
+			return;
+		}
+		Location currLocation = backtrace[backtrace.size() - 1];
+		if (currLocation != lastLocation)
+		{
+			rr::Print("rr> {0} [{1}:{2}]\n", currLocation.function.name.c_str(), currLocation.function.file.c_str(), currLocation.line);
+			lastLocation = std::move(currLocation);
+		}
+#endif // ENABLE_RR_EMIT_PRINT_LOCATION
 	}
 
 	void DebugInfo::Flush()
@@ -178,7 +192,7 @@
 			size_t size = sizeof(buf);
 			int status = 0;
 			llvm::itaniumDemangle(location.function.name.c_str(), buf, &size, &status);
-			auto name = status == 0 ? buf : location.function.name.c_str();
+			auto name = "jit!" + (status == 0 ? std::string(buf) : location.function.name);
 
 			auto func = diBuilder->createFunction(
 				file,                           // scope
@@ -414,48 +428,41 @@
 
 	DebugInfo::Backtrace DebugInfo::getCallerBacktrace(size_t limit /* = 0 */) const
 	{
-		struct callbacks
-		{
-			static void onError(void *data, const char *msg, int errnum)
-			{
-				fprintf(stderr, "BACKTRACE ERROR %d: %s\n", errnum, msg);
-			}
-
-			static int onPCInfo(void *data, uintptr_t pc, const char *file, int line, const char *function)
-			{
-				if (file == nullptr) { return 0; }
-
-				auto const &fileSR = llvm::StringRef(file);
-				if (fileSR.endswith("ReactorDebugInfo.cpp") ||
-					fileSR.endswith("Reactor.cpp") ||
-					fileSR.endswith("Reactor.hpp"))
-				{
-					return 0;
-				}
-
-				auto cb = reinterpret_cast<callbacks*>(data);
-
-				Location location;
-				location.function.file = file;
-				location.function.name = function;
-				location.line = line;
-
-				cb->locations.push_back(location);
-				return (cb->limit == 0 || sizeof(cb->locations) < cb->limit) ? 0 : 1;
-			}
-
-			size_t limit;
-			std::vector<DebugInfo::Location> locations;
+		auto shouldSkipFile = [](llvm::StringRef fileSR) {
+				return fileSR.empty() ||
+					fileSR.endswith_lower("ReactorDebugInfo.cpp") ||
+					fileSR.endswith_lower("Reactor.cpp") ||
+					fileSR.endswith_lower("Reactor.hpp") ||
+					fileSR.endswith_lower("stacktrace.hpp");
 		};
 
-		callbacks callbacks;
-		callbacks.limit = limit;
-		static auto state = backtrace_create_state(nullptr, 0, &callbacks::onError, nullptr);
-		backtrace_full(state, 1, &callbacks::onPCInfo, &callbacks::onError, &callbacks);
+		std::vector<DebugInfo::Location> locations;
 
-		std::reverse(callbacks.locations.begin(), callbacks.locations.end());
+		// Note that bs::stacktrace() effectively returns a vector of addresses; bs::frame construction is where
+		// the heavy lifting is done: resolving the function name, file and line number.
+		namespace bs = boost::stacktrace;
+		for (bs::frame frame : bs::stacktrace())
+		{
+			if (shouldSkipFile(frame.source_file()))
+			{
+				continue;
+			}
 
-		return callbacks.locations;
+			DebugInfo::Location location;
+			location.function.file = frame.source_file();
+			location.function.name = frame.name();
+			location.line = frame.source_line();
+			locations.push_back(location);
+
+			if (limit > 0 && locations.size() >= limit)
+			{
+				break;
+			}
+		}
+
+		std::reverse(locations.begin(), locations.end());
+
+		return locations;
 	}
 
 	llvm::DIType *DebugInfo::getOrCreateType(llvm::Type* type)
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 5cf1be0..8ac97d5 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -3420,7 +3420,11 @@
 	//
 	// RR_LOG() is intended to be used for debugging JIT compiled code, and is
 	// not intended for production use.
-	#define RR_LOG(msg, ...) Print(__PRETTY_FUNCTION__, __FILE__, __LINE__, msg "\n", ##__VA_ARGS__)
+	#if defined(_WIN32)
+		#define RR_LOG(msg, ...) Print(__FUNCSIG__, __FILE__, __LINE__, msg "\n", ##__VA_ARGS__)
+	#else
+		#define RR_LOG(msg, ...) Print(__PRETTY_FUNCTION__, __FILE__, __LINE__, msg "\n", ##__VA_ARGS__)
+	#endif
 
 	// Macro magic to perform variadic dispatch.
 	// See: https://renenyffenegger.ch/notes/development/languages/C-C-plus-plus/preprocessor/macros/__VA_ARGS__/count-arguments