PYTHON ?= python3
GDB ?= gdb
LLDB ?= lldb

# Default to the most common build output directories generated by
# gm.py for convenience, but allow overriding for custom setups.
ifeq ($(shell uname -s),Darwin)
OUT_DIR ?= ../../../out/arm64.release
DEBUG_HELPER_LIB ?= $(OUT_DIR)/libv8_debug_helper.dylib
else
OUT_DIR ?= ../../../out/x64.release
DEBUG_HELPER_LIB ?= $(OUT_DIR)/libv8_debug_helper.so
endif

D8 ?= $(dir $(DEBUG_HELPER_LIB))d8
CORRUPTION_BIN ?= $(dir $(DEBUG_HELPER_LIB))corruption_harness
TEST_DIR ?= ./test
FIXTURES_DIR ?= $(TEST_DIR)/fixtures
BACKTRACE_SCRIPT ?= $(FIXTURES_DIR)/throw.js
CORRUPTION_SCRIPT ?= $(FIXTURES_DIR)/invalid-script-source.js
# d8 enables --sandbox-prohibit-insecure-mode on macOS/Linux, which makes
# sandbox initialization abort with a fatal OOM when launched under a debugger
# that disable ASLR by default. Disable it to make it load in debuggers.
D8_ARGS ?= --abort-on-uncaught-exception --no-sandbox-prohibit-insecure-mode
GDBINIT ?= ../../gdbinit
GDB_PLUGIN ?= ./gdb_plugin.py
LLDB_PLUGIN ?= ./lldb_plugin.py
TEST_PACKAGE ?= test
CORE_DIR ?= $(OUT_DIR)/debug_helper.cores
BACKTRACE_CORE ?= $(CORE_DIR)/throw.core
CORRUPTION_CORE ?= $(CORE_DIR)/$(basename $(notdir $(CORRUPTION_SCRIPT))).core
CORRUPTION_CORE_CASES := invalid-script-source invalid-shared-function-info
CORRUPTION_CORES := $(foreach case,$(CORRUPTION_CORE_CASES),$(CORE_DIR)/$(case).core)

define generate_core_gdb
	mkdir -p "$(dir $(3))"
	rm -f "$(3)"
	V8_DEBUG_HELPER_LIB_PATH="$(abspath $(DEBUG_HELPER_LIB))" \
	$(GDB) -nx -q -batch \
		-iex 'set debuginfod enabled off' \
		-ex 'file "$(1)"' \
		-ex 'run $(2)' \
		-ex 'generate-core-file $(3)' \
		-ex 'quit'
endef

define generate_core_lldb
	mkdir -p "$(dir $(3))"
	rm -f "$(3)"
	V8_DEBUG_HELPER_LIB_PATH="$(abspath $(DEBUG_HELPER_LIB))" \
	$(LLDB) -b \
		-O 'target create "$(1)"' \
		-O 'settings set -- target.run-args $(2)' \
		-O 'run' \
		-k 'process save-core --style full "$(3)"' \
		-k 'quit'
endef

ifeq ($(shell uname -s),Darwin)
define generate_core
	$(call generate_core_lldb,$(1),$(2),$(3))
endef
else
define generate_core
	$(call generate_core_gdb,$(1),$(2),$(3))
endef
endif

define run_gdb_live
	V8_DEBUG_HELPER_LIB_PATH="$(abspath $(DEBUG_HELPER_LIB))" \
	$(GDB) -nx -q -batch \
		-iex 'set debuginfod enabled off' \
		-iex 'source $(abspath $(GDBINIT))' \
		-iex 'source $(abspath $(GDB_PLUGIN))' \
		-ex 'file "$(1)"' \
		-ex 'run $(2)' \
		-ex 'bt' \
		-ex 'quit'
endef

define run_lldb_live
	V8_DEBUG_HELPER_LIB_PATH="$(abspath $(DEBUG_HELPER_LIB))" \
	$(LLDB) -b \
		-O 'command script import "$(abspath $(LLDB_PLUGIN))"' \
		-O 'target create "$(1)"' \
		-O 'settings set -- target.run-args $(2)' \
		-O 'run' \
		-k 'bt' \
		-k 'quit'
endef

define run_gdb_core
	V8_DEBUG_HELPER_LIB_PATH="$(abspath $(DEBUG_HELPER_LIB))" \
	$(GDB) -nx -q -batch \
		-iex 'set debuginfod enabled off' \
		-iex 'source $(abspath $(GDBINIT))' \
		-iex 'source $(abspath $(GDB_PLUGIN))' \
		-ex 'file "$(1)"' \
		-ex 'core-file $(2)' \
		-ex 'bt' \
		-ex 'quit'
endef

define run_lldb_core
	V8_DEBUG_HELPER_LIB_PATH="$(abspath $(DEBUG_HELPER_LIB))" \
	$(LLDB) -b \
		-O 'command script import "$(abspath $(LLDB_PLUGIN))"' \
		-O 'target create --core "$(2)" "$(1)"' \
		-O 'bt' \
		-O 'quit'
endef

define run_gdb_test
	GDB='$(GDB)' \
	GDB_PLUGIN='$(abspath $(GDB_PLUGIN))' \
	GDBINIT='$(abspath $(GDBINIT))' \
	D8='$(abspath $(D8))' \
	CORRUPTION_BIN='$(abspath $(CORRUPTION_BIN))' \
	DEBUG_HELPER_LIB='$(abspath $(DEBUG_HELPER_LIB))' \
	CORE_DIR='$(abspath $(CORE_DIR))' \
	$(PYTHON) -m unittest -v $(TEST_PACKAGE).$(1)
endef

define run_lldb_test
	LLDB='$(LLDB)' \
	LLDB_PLUGIN='$(abspath $(LLDB_PLUGIN))' \
	D8='$(abspath $(D8))' \
	CORRUPTION_BIN='$(abspath $(CORRUPTION_BIN))' \
	DEBUG_HELPER_LIB='$(abspath $(DEBUG_HELPER_LIB))' \
	CORE_DIR='$(abspath $(CORE_DIR))' \
	$(PYTHON) -m unittest -v $(TEST_PACKAGE).$(1)
endef

.PHONY: run-live-gdb run-live-backtrace-gdb run-live-corruption-gdb run-live-lldb run-live-backtrace-lldb run-live-corruption-lldb run-live run-core-gdb run-core-backtrace-gdb run-core-corruption-gdb run-core-lldb run-core-backtrace-lldb run-core-corruption-lldb run-core run prepare-cores clean-cores test-live-gdb test-live-backtrace-gdb test-live-corruption-gdb test-live-lldb test-live-backtrace-lldb test-live-corruption-lldb test-live test-core-gdb test-core-lldb test-core test

$(BACKTRACE_CORE): $(BACKTRACE_SCRIPT) $(D8)
	$(call generate_core,$(abspath $(D8)),$(D8_ARGS) "$(abspath $(BACKTRACE_SCRIPT))",$(abspath $@))

# The corruption cores depend on the harness binary too, so they get
# regenerated whenever the harness changes.
$(CORE_DIR)/%.core: $(FIXTURES_DIR)/%.js $(CORRUPTION_BIN)
	$(call generate_core,$(abspath $(CORRUPTION_BIN)),"$(abspath $(FIXTURES_DIR))/$*.js",$(abspath $@))

run-live-gdb: run-live-backtrace-gdb run-live-corruption-gdb

run-live-backtrace-gdb:
	$(call run_gdb_live,$(abspath $(D8)),$(D8_ARGS) "$(abspath $(BACKTRACE_SCRIPT))")

run-live-lldb: run-live-backtrace-lldb run-live-corruption-lldb

run-live-backtrace-lldb:
	$(call run_lldb_live,$(abspath $(D8)),$(D8_ARGS) "$(abspath $(BACKTRACE_SCRIPT))")

run-live-corruption-gdb:
	$(call run_gdb_live,$(abspath $(CORRUPTION_BIN)),"$(abspath $(CORRUPTION_SCRIPT))")

run-live-corruption-lldb:
	$(call run_lldb_live,$(abspath $(CORRUPTION_BIN)),"$(abspath $(CORRUPTION_SCRIPT))")

run-core-gdb: run-core-backtrace-gdb run-core-corruption-gdb

run-core-backtrace-gdb: $(BACKTRACE_CORE)
	$(call run_gdb_core,$(abspath $(D8)),$(abspath $(BACKTRACE_CORE)))

run-core-corruption-gdb: $(CORRUPTION_CORE)
	$(call run_gdb_core,$(abspath $(CORRUPTION_BIN)),$(abspath $(CORRUPTION_CORE)))

run-core-lldb: run-core-backtrace-lldb run-core-corruption-lldb

run-core-backtrace-lldb: $(BACKTRACE_CORE)
	$(call run_lldb_core,$(abspath $(D8)),$(abspath $(BACKTRACE_CORE)))

run-core-corruption-lldb: $(CORRUPTION_CORE)
	$(call run_lldb_core,$(abspath $(CORRUPTION_BIN)),$(abspath $(CORRUPTION_CORE)))

run-core: run-core-gdb run-core-lldb

prepare-cores: $(BACKTRACE_CORE) $(CORRUPTION_CORES)

clean-cores:
	rm -rf "$(CORE_DIR)"

test-live-gdb:
	$(call run_gdb_test,test_gdb_live)

test-live-backtrace-gdb:
	$(call run_gdb_test,test_gdb_live.GdbBacktraceTest)

test-live-corruption-gdb:
	$(call run_gdb_test,test_gdb_live.GdbCorruptionTest)

test-live-lldb:
	$(call run_lldb_test,test_lldb_live)

test-live-backtrace-lldb:
	$(call run_lldb_test,test_lldb_live.LldbBacktraceTest)

test-live-corruption-lldb:
	$(call run_lldb_test,test_lldb_live.LldbCorruptionTest)

test-core-gdb: prepare-cores
	$(call run_gdb_test,test_gdb_core)

test-core-lldb: prepare-cores
	$(call run_lldb_test,test_lldb_core)

test-live: test-live-gdb test-live-lldb

test-core: test-core-gdb test-core-lldb

test: test-live
