diff --git a/.classpath b/.classpath index 3de5a861a7..0a09fb80a2 100644 --- a/.classpath +++ b/.classpath @@ -13,6 +13,7 @@ + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd591a056c..a6988de34c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,17 +8,17 @@ jobs: fail-fast: false # we want all jobs to run, because they may fail independently matrix: include: - - os: macos-latest - job: test-osx + # - os: macos-latest + # job: test-osx - os: ubuntu-20.04 job: test-1 - - os: ubuntu-20.04 - job: test-2 + # - os: ubuntu-20.04 + # job: test-2 - - os: ubuntu-20.04 - job: lint + # - os: ubuntu-20.04 + # job: lint runs-on: ubuntu-20.04 # ubuntu-latest @@ -28,11 +28,11 @@ jobs: with: submodules: true - - name: Compile SOMns Jar and Unit Tests + - name: Compile SOMns if: matrix.job != 'lint' run: | export JAVA_HOME=$JAVA_HOME_17_X64 - ant -e jar unit-tests + ant -e jar - name: SOMns Test Suite if: matrix.job != 'lint' @@ -40,43 +40,49 @@ jobs: export JAVA_HOME=$JAVA_HOME_17_X64 ./som -G core-lib/TestSuite/TestRunner.ns - - name: Build Native Image + - name: SOMns Test Suite - Async Stack Traces if: matrix.job == 'test-1' run: | export JAVA_HOME=$JAVA_HOME_17_X64 - ant -e native-tests - - - name: Kompos Tests - if: matrix.job == 'test-2' - run: | - export JAVA_HOME=$JAVA_HOME_17_X64 - ant -e kompos - cd tools/kompos - npm test - - - name: Dynamic Metrics Tests - if: matrix.job == 'test-osx' - run: | - export JAVA_HOME=$JAVA_HOME_17_X64 - ant -e dynamic-metrics-tests - - - name: Super Instructions Tests - if: matrix.job == 'test-2' - run: | - export JAVA_HOME=$JAVA_HOME_17_X64 - ant -e superinstructions-tests - - - name: Replay Tests 1 - if: matrix.job == 'test-1' - run: | - export JAVA_HOME=$JAVA_HOME_17_X64 - ./tests/replay/test.sh 1 - - - name: Replay Tests 2 - if: matrix.job == 'test-2' - run: | - export JAVA_HOME=$JAVA_HOME_17_X64 - ./tests/replay/test.sh 2 + ./som -G -asts -astic core-lib/TestSuite/TestRunner.ns + + # - name: Build Native Image + # if: matrix.job == 'test-1' + # run: | + # export JAVA_HOME=$JAVA_HOME_17_X64 + # ant -e native-tests + + # - name: Kompos Tests + # if: matrix.job == 'test-2' + # run: | + # export JAVA_HOME=$JAVA_HOME_17_X64 + # ant -e kompos + # cd tools/kompos + # npm test + + # - name: Dynamic Metrics Tests + # if: matrix.job == 'test-osx' + # run: | + # export JAVA_HOME=$JAVA_HOME_17_X64 + # ant -e dynamic-metrics-tests + + # - name: Super Instructions Tests + # if: matrix.job == 'test-2' + # run: | + # export JAVA_HOME=$JAVA_HOME_17_X64 + # ant -e superinstructions-tests + + # - name: Replay Tests 1 + # if: matrix.job == 'test-1' + # run: | + # export JAVA_HOME=$JAVA_HOME_17_X64 + # ./tests/replay/test.sh 1 + + # - name: Replay Tests 2 + # if: matrix.job == 'test-2' + # run: | + # export JAVA_HOME=$JAVA_HOME_17_X64 + # ./tests/replay/test.sh 2 # Disabled due to breaking changes in the tracing infrastructure. Not fixed to make merge of Snapshotting PR(#293) easier. #- name: Snapshot Tests diff --git a/.gitmodules b/.gitmodules index 1e23dd6880..b708aa11e2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,7 @@ path = libs/truffle url = https://github.com/smarr/Truffle.git shallow = false + branch = debugger/step-end-turn [submodule "libs/mx"] path = libs/mx url = https://github.com/smarr/mx.git diff --git a/.settings/SOMns-tests.launch b/.settings/SOMns-tests.launch index b744733820..b898f7e828 100644 --- a/.settings/SOMns-tests.launch +++ b/.settings/SOMns-tests.launch @@ -1,21 +1,25 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 8556c967c0..098fc97fae 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -44,7 +44,7 @@ org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_assertion_message=16 +org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0 org.eclipse.jdt.core.formatter.alignment_for_assignment=16 org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 diff --git a/build.xml b/build.xml index 3168d7de9e..a1c5a7cc07 100644 --- a/build.xml +++ b/build.xml @@ -311,6 +311,7 @@ kernel: ${kernel} + diff --git a/codespeed.conf b/codespeed.conf index 3fd67fca23..0f810d7fd5 100644 --- a/codespeed.conf +++ b/codespeed.conf @@ -3,14 +3,14 @@ default_data_file: 'codespeed.data' default_experiment: SOMns -reporting: - # Benchmark results will be reported to ReBenchDB - rebenchdb: - # this url needs to point to the API endpoint - db_url: https://rebench.stefan-marr.de/rebenchdb - repo_url: https://github.com/smarr/SOMns - record_all: true # make sure everything is recorded - project_name: SOMns +# reporting: +# # Benchmark results will be reported to ReBenchDB +# rebenchdb: +# # this url needs to point to the API endpoint +# db_url: https://rebench.stefan-marr.de/rebenchdb +# repo_url: https://github.com/smarr/SOMns +# record_all: true # make sure everything is recorded +# project_name: SOMns runs: min_iteration_time: 5 @@ -26,51 +26,51 @@ benchmark_suites: - CD: extra_args: "1 0 2" codespeed_name: "1st.CD" - machines: [yuria ] + # machines: [yuria ] - Havlak: extra_args: "1 0 1" codespeed_name: "1st.Havlak" - machines: [yuria2] + # machines: [yuria2] - Richards: extra_args: "1 0 1" codespeed_name: "1st.Richards" - machines: [yuria3] + # machines: [yuria3] - RichardsNS: extra_args: "1 0 1" codespeed_name: "1st.RichardsNS" - machines: [yuria] + # machines: [yuria] - DeltaBlue: extra_args: "1 0 200" codespeed_name: "1st.DeltaBlue" - machines: [yuria2] + # machines: [yuria2] - DeltaBlueNS: extra_args: "1 0 200" codespeed_name: "1st.DeltaBlueNS" - machines: [yuria3] + # machines: [yuria3] - Mandelbrot: extra_args: "1 0 50" codespeed_name: "1st.Mandelbrot" - machines: [yuria ] + # machines: [yuria ] - NBody: extra_args: "1 0 10000" codespeed_name: "1st.NBody" - machines: [yuria2] + # machines: [yuria2] - Json: extra_args: "1 0 2" codespeed_name: "1st.Json" - machines: [yuria3] + # machines: [yuria3] - GraphSearch: extra_args: "1 0 1" codespeed_name: "1st.GraphSearch" - machines: [yuria ] + # machines: [yuria ] - PageRank: extra_args: "1 0 100" codespeed_name: "1st.PageRank" - machines: [yuria2] + # machines: [yuria2] - Splay: extra_args: "1 0 1" codespeed_name: "1st.Splay" - machines: [yuria3] + # machines: [yuria3] macro-steady: gauge_adapter: RebenchLog @@ -81,58 +81,58 @@ benchmark_suites: - CD: extra_args: "130 0 100" codespeed_name: "peak.CD" - machines: [yuria ] + # machines: [yuria ] - Havlak: extra_args: "130 0 5" codespeed_name: "peak.Havlak" - machines: [yuria2] + # machines: [yuria2] - Richards: extra_args: "130 0 30" codespeed_name: "peak.Richards" - machines: [yuria3] + # machines: [yuria3] - RichardsNS: extra_args: "130 0 200" codespeed_name: "peak.RichardsNS" - machines: [yuria ] + # machines: [yuria ] - DeltaBlue: extra_args: "250 0 10000" codespeed_name: "peak.DeltaBlue" warmup: 150 - machines: [yuria2] + # machines: [yuria2] - DeltaBlueNS: extra_args: "250 0 1000" codespeed_name: "peak.DeltaBlue" warmup: 150 - machines: [yuria3] + # machines: [yuria3] - Mandelbrot: extra_args: "110 0 400" codespeed_name: "peak.Mandelbrot" warmup: 10 - machines: [yuria ] + # machines: [yuria ] - NBody: extra_args: "120 0 150000" codespeed_name: "peak.NBody" warmup: 20 - machines: [yuria2] + # machines: [yuria2] - Json: extra_args: "120 0 50" codespeed_name: "peak.Json" warmup: 20 - machines: [yuria3] + # machines: [yuria3] - GraphSearch: extra_args: "250 0 15" codespeed_name: "peak.GraphSearch" warmup: 100 - machines: [yuria ] + # machines: [yuria ] - PageRank: extra_args: "120 0 1000" codespeed_name: "peak.PageRank" warmup: 20 - machines: [yuria2] + # machines: [yuria2] - Splay: extra_args: "150 0 1" codespeed_name: "peak.Splay" - machines: [yuria3] + # machines: [yuria3] som-startup: gauge_adapter: RebenchLog @@ -143,35 +143,35 @@ benchmark_suites: - Fannkuch: extra_args: "1 0 7" codespeed_name: "1st.Fannkuch" - machines: [yuria ] + # machines: [yuria ] - List: extra_args: "1 0 50" codespeed_name: "1st.List" - machines: [yuria2] + # machines: [yuria2] - Bounce: extra_args: "1 0 20" codespeed_name: "1st.Bounce" - machines: [yuria3] + # machines: [yuria3] - Permute: extra_args: "1 0 10" codespeed_name: "1st.Permute" - machines: [yuria ] + # machines: [yuria ] - Queens: extra_args: "1 0 10" codespeed_name: "1st.Queens" - machines: [yuria2] + # machines: [yuria2] - Storage: extra_args: "1 0 20" codespeed_name: "1st.Storage" - machines: [yuria3] + # machines: [yuria3] - Sieve: extra_args: "1 0 100" codespeed_name: "1st.Sieve" - machines: [yuria ] + # machines: [yuria ] - Towers: extra_args: "1 0 20" codespeed_name: "1st.Towers" - machines: [yuria2] + # machines: [yuria2] som-steady: gauge_adapter: RebenchLog @@ -183,38 +183,38 @@ benchmark_suites: extra_args: "55 0 9" codespeed_name: "peak.Fannkuch" warmup: 5 - machines: [yuria3] + # machines: [yuria3] - List: extra_args: "70 0 1000" codespeed_name: "peak.List" warmup: 20 - machines: [yuria ] + # machines: [yuria ] - Bounce: extra_args: "60 0 1000" codespeed_name: "peak.Bounce" - machines: [yuria2] + # machines: [yuria2] - Permute: extra_args: "60 0 500" codespeed_name: "peak.Permute" - machines: [yuria3] + # machines: [yuria3] - Queens: extra_args: "120 0 400" codespeed_name: "peak.Queens" warmup: 70 - machines: [yuria ] + # machines: [yuria ] - Storage: extra_args: "75 0 700" codespeed_name: "peak.Storage" warmup: 25 - machines: [yuria2] + # machines: [yuria2] - Sieve: extra_args: "60 0 400" codespeed_name: "peak.Sieve" - machines: [yuria3] + # machines: [yuria3] - Towers: extra_args: "60 0 300" codespeed_name: "peak.Towers" - machines: [yuria ] + # machines: [yuria ] sort-startup: gauge_adapter: RebenchLog @@ -225,15 +225,15 @@ benchmark_suites: - TreeSort: extra_args: "1 0 10" codespeed_name: "1st.TreeSort" - machines: [yuria2] + # machines: [yuria2] - BubbleSort: extra_args: "1 0 25" codespeed_name: "1st.BubbleSort" - machines: [yuria3] + # machines: [yuria3] - QuickSort: extra_args: "1 0 20" codespeed_name: "1st.QuickSort" - machines: [yuria ] + # machines: [yuria ] sort-steady: gauge_adapter: RebenchLog @@ -244,15 +244,15 @@ benchmark_suites: - BubbleSort: extra_args: "60 0 2000" codespeed_name: "peak.BubbleSort" - machines: [yuria2] + # machines: [yuria2] - QuickSort: extra_args: "60 0 800" codespeed_name: "peak.QuickSort" - machines: [yuria3] + # machines: [yuria3] - TreeSort: extra_args: "60 0 300" codespeed_name: "peak.TreeSort" - machines: [yuria ] + # machines: [yuria ] micro-startup: gauge_adapter: RebenchLog @@ -263,43 +263,43 @@ benchmark_suites: - Fibonacci: extra_args: "1 0 100" codespeed_name: "1st.Fibonacci" - machines: [yuria2] + # machines: [yuria2] - ClosureDefFibonacci: extra_args: "1 0 50" codespeed_name: "1st.ClosureDefFibonacci" - machines: [yuria3] + # machines: [yuria3] - ClosureFibonacci: extra_args: "1 0 50" codespeed_name: "1st.ClosureFibonacci" - machines: [yuria ] + # machines: [yuria ] - Dispatch: extra_args: "1 0 50" codespeed_name: "1st.Dispatch" - machines: [yuria2] + # machines: [yuria2] - Loop: extra_args: "1 0 100" codespeed_name: "1st.Loop" - machines: [yuria3] + # machines: [yuria3] - Recurse: extra_args: "1 0 50" codespeed_name: "1st.Recurse" - machines: [yuria ] + # machines: [yuria ] - Sum: extra_args: "1 0 100" codespeed_name: "1st.Sum" - machines: [yuria2] + # machines: [yuria2] - IntegerLoop: extra_args: "1 0 100" codespeed_name: "1st.IntegerLoop" - machines: [yuria3] + # machines: [yuria3] - FieldLoop: extra_args: "1 0 2" codespeed_name: "1st.FieldLoop" - machines: [yuria ] + # machines: [yuria ] - WhileLoop: extra_args: "1 0 100" codespeed_name: "1st.WhileLoop" - machines: [yuria2] + # machines: [yuria2] micro-steady: gauge_adapter: RebenchLog @@ -311,46 +311,46 @@ benchmark_suites: extra_args: "70 0 1000" codespeed_name: "peak.Fibonacci" warmup: 20 - machines: [yuria3] + # machines: [yuria3] - ClosureDefFibonacci: extra_args: "70 0 300" codespeed_name: "peak.ClosureDefFibonacci" warmup: 20 - machines: [yuria ] + # machines: [yuria ] - ClosureFibonacci: extra_args: "70 0 300" codespeed_name: "peak.ClosureFibonacci" warmup: 20 - machines: [yuria2] + # machines: [yuria2] - Dispatch: extra_args: "55 0 2000" codespeed_name: "peak.Dispatch" - machines: [yuria3] + # machines: [yuria3] - Loop: extra_args: "55 0 500000" codespeed_name: "peak.Loop" - machines: [yuria ] + # machines: [yuria ] - Recurse: extra_args: "70 0 1000" codespeed_name: "peak.Recurse" warmup: 20 - machines: [yuria2] + # machines: [yuria2] - Sum: extra_args: "55 0 3000" codespeed_name: "peak.Sum" - machines: [yuria3] + # machines: [yuria3] - IntegerLoop: extra_args: "55 0 2000" codespeed_name: "peak.IntegerLoop" - machines: [yuria ] + # machines: [yuria ] - FieldLoop: extra_args: "55 0 200" codespeed_name: "peak.FieldLoop" - machines: [yuria2] + # machines: [yuria2] - WhileLoop: extra_args: "55 0 2000" codespeed_name: "peak.WhileLoop" - machines: [yuria3] + # machines: [yuria3] savina-interp: gauge_adapter: RebenchLog @@ -362,87 +362,87 @@ benchmark_suites: - PingPong: extra_args: 20000 codespeed_name: "M.PingPong" - machines: [yuria ] + # machines: [yuria ] - Counting: extra_args: 100000 # was 1000000 codespeed_name: "M.Counting" - machines: [yuria2] + # machines: [yuria2] - ForkJoinThroughput: extra_args: "1000:60" # "10000:60" codespeed_name: "M.ForkJoinThroughput" - machines: [yuria3] + # machines: [yuria3] - ForkJoinActorCreation: extra_args: 10000 codespeed_name: "M.ForkJoinActorCreation" - machines: [yuria ] + # machines: [yuria ] - ThreadRing: extra_args: "100:50000" codespeed_name: "M.ThreadRing" - machines: [yuria2] + # machines: [yuria2] - Chameneos: extra_args: "100:10000" # "100:200000" codespeed_name: "M.Chameneos" - machines: [yuria3] + # machines: [yuria3] - BigContention: extra_args: "100:120" codespeed_name: "M.BigContention" - machines: [yuria ] + # machines: [yuria ] # Concurrency - ConcurrentDictionary: extra_args: "20:600:20" # "20:10000:50" codespeed_name: "C.Dictionary" - machines: [yuria2] + # machines: [yuria2] - ConcurrentSortedLinkedList: extra_args: "10:300:10:1" # "20:8000:10:1" codespeed_name: "C.SortedLinkedList" - machines: [yuria3] + # machines: [yuria3] - ProducerConsumerBoundedBuffer: extra_args: "40:5:5:10" # "50:40:40:1000" codespeed_name: "C.ProdConBoundedBuffer" - machines: [yuria ] + # machines: [yuria ] - Philosophers: extra_args: "20:1000" codespeed_name: "C.Philosophers" - machines: [yuria2] + # machines: [yuria2] - SleepingBarber: extra_args: "800:400:400:200" codespeed_name: "C.SleepingBarber" - machines: [yuria3] + # machines: [yuria3] - CigaretteSmokers: extra_args: "1000:200" codespeed_name: "C.CigaretteSmokers" - machines: [yuria ] + # machines: [yuria ] - LogisticsMapSeries: extra_args: "2000:10:346" codespeed_name: "C.LogisticsMapSeries" - machines: [yuria2] + # machines: [yuria2] - BankTransaction: extra_args: "1000:1000" codespeed_name: "C.BankTransaction" - machines: [yuria3] + # machines: [yuria3] # Parallelism - RadixSort: extra_args: "10000:65536:74755" # "100000:1152921504606846976:74755" codespeed_name: "P.RadixSort" - machines: [yuria ] + # machines: [yuria ] - UnbalancedCobwebbedTree: extra_args: "5000:10:0:1" codespeed_name: "P.UnbalancedCobwebbedTree" - machines: [yuria2] + # machines: [yuria2] - TrapezoidalApproximation: extra_args: "100:10000:1:5" # "100:10000000:1:5" codespeed_name: "P.TrapezoidalApproximation" - machines: [yuria3] + # machines: [yuria3] - AStarSearch: extra_args: "100:10" codespeed_name: "P.AStarSearch" - machines: [yuria ] + # machines: [yuria ] - NQueens: extra_args: "20:8:4" codespeed_name: "P.NQueens" - machines: [yuria2] + # machines: [yuria2] savina-jit: gauge_adapter: RebenchLog @@ -454,87 +454,87 @@ benchmark_suites: - PingPong: extra_args: 40000 codespeed_name: "M.PingPong" - machines: [yuria3] + # machines: [yuria3] - Counting: extra_args: 200000 # was 1000000 codespeed_name: "M.Counting" - machines: [yuria ] + # machines: [yuria ] - ForkJoinThroughput: extra_args: "3000:60" # "10000:60" codespeed_name: "M.ForkJoinThroughput" - machines: [yuria2] + # machines: [yuria2] - ForkJoinActorCreation: extra_args: 40000 codespeed_name: "M.ForkJoinActorCreation" - machines: [yuria3] + # machines: [yuria3] - ThreadRing: extra_args: "100:100000" codespeed_name: "M.ThreadRing" - machines: [yuria ] + # machines: [yuria ] - Chameneos: extra_args: "100:100000" # "100:200000" codespeed_name: "M.Chameneos" - machines: [yuria2] + # machines: [yuria2] - BigContention: extra_args: "800:120" codespeed_name: "M.BigContention" - machines: [yuria3] + # machines: [yuria3] # Concurrency - ConcurrentDictionary: extra_args: "20:1000:20" # "20:10000:50" codespeed_name: "C.Dictionary" - machines: [yuria ] + # machines: [yuria ] - ConcurrentSortedLinkedList: extra_args: "10:1000:10:1" # "20:8000:10:1" codespeed_name: "C.SortedLinkedList" - machines: [yuria2] + # machines: [yuria2] - ProducerConsumerBoundedBuffer: extra_args: "40:5:5:10" # "50:40:40:1000" codespeed_name: "C.ProdConBoundedBuffer" - machines: [yuria3] + # machines: [yuria3] - Philosophers: extra_args: "20:5000" codespeed_name: "C.Philosophers" - machines: [yuria ] + # machines: [yuria ] - SleepingBarber: extra_args: "1000:1000:1000:500" codespeed_name: "C.SleepingBarber" - machines: [yuria2] + # machines: [yuria2] - CigaretteSmokers: extra_args: "10000:200" codespeed_name: "C.CigaretteSmokers" - machines: [yuria3] + # machines: [yuria3] - LogisticsMapSeries: extra_args: "10000:10:346" codespeed_name: "C.LogisticsMapSeries" - machines: [yuria ] + # machines: [yuria ] - BankTransaction: extra_args: "1000:50000" codespeed_name: "C.BankTransaction" - machines: [yuria2] + # machines: [yuria2] # Parallelism - RadixSort: extra_args: "10000:65536:74755" # "100000:1152921504606846976:74755" codespeed_name: "P.RadixSort" - machines: [yuria3] + # machines: [yuria3] - UnbalancedCobwebbedTree: extra_args: "30000:10:500:100" codespeed_name: "P.UnbalancedCobwebbedTree" - machines: [yuria ] + # machines: [yuria ] - TrapezoidalApproximation: extra_args: "100:100000:1:5" # "100:10000000:1:5" codespeed_name: "P.TrapezoidalApproximation" - machines: [yuria2] + # machines: [yuria2] - AStarSearch: extra_args: "100:20" codespeed_name: "P.AStarSearch" - machines: [yuria3] + # machines: [yuria3] - NQueens: extra_args: "20:9:4" codespeed_name: "P.NQueens" - machines: [yuria ] + # machines: [yuria ] validation: gauge_adapter: ValidationLog @@ -572,19 +572,19 @@ benchmark_suites: - CilkSort: extra_args: "130 0 50" codespeed_name: "peak.CilkSort" - machines: [yuria2] + # machines: [yuria2] - Integrate: extra_args: "130 0 100" codespeed_name: "peak.Integrate" - machines: [yuria3] + # machines: [yuria3] - Jacobi: extra_args: "130 0 400" codespeed_name: "peak.Jacobi" - machines: [yuria ] + # machines: [yuria ] - LUDecomposition: extra_args: "130 0 256" codespeed_name: "peak.LUDecomposition" - machines: [yuria2] + # machines: [yuria2] fj-seq-startup: description: Sequential version of Fork/Join benchmarks for normal performance tracking @@ -600,19 +600,19 @@ benchmark_suites: - CilkSort: extra_args: "1 0 1" codespeed_name: "1st.CilkSort" - machines: [yuria3] + # machines: [yuria3] - Integrate: extra_args: "1 0 50" codespeed_name: "1st.Integrate" - machines: [yuria ] + # machines: [yuria ] - Jacobi: extra_args: "1 0 100" codespeed_name: "1st.Jacobi" - machines: [yuria2] + # machines: [yuria2] - LUDecomposition: extra_args: "1 0 128" codespeed_name: "1st.LUDecomposition" - machines: [yuria3] + # machines: [yuria3] forkjoin: gauge_adapter: RebenchLog @@ -657,17 +657,17 @@ benchmark_suites: #numMessages numThreads, numThreads is unused extra_args: "10000 2" codespeed_name: "CSP.PingPong" - machines: [yuria ] + # machines: [yuria ] - SavinaCSP.ForkJoinThroughput: #numMessages numThreads extra_args: "8000 4" - machines: [yuria2] + # machines: [yuria2] codespeed_name: "CSP.ForkJoinThroughput" - SavinaCSP.Philosophers: #numrounds numThreads, uses numThreads - 1 Philosophers extra_args: "200 4" codespeed_name: "CSP.Philosophers" - machines: [yuria3] + # machines: [yuria3] csp-startup: description: CSP port of the savina benchmarks @@ -719,17 +719,17 @@ benchmark_suites: - MutexSuite.ProducerConsumer: #buffersize numThreads, split threads equally into producers and consumers extra_args: "4000 4" - machines: [yuria ] + # machines: [yuria ] - MutexSuite.Philosophers: #numrounds numThreads, uses numThreads - 1 Philosophers extra_args: "50000 4" - machines: [yuria2] + # machines: [yuria2] - Lee: extra_args: 2 - machines: [yuria3] + # machines: [yuria3] - Vacation: extra_args: 12 - machines: [yuria ] + # machines: [yuria ] stm-startup: description: various mutex based benchmarks @@ -755,10 +755,10 @@ benchmark_suites: benchmarks: - LeeSTM: extra_args: 4 - machines: [yuria2] + # machines: [yuria2] - VacationSTM: extra_args: 10 - machines: [yuria3] + # machines: [yuria3] # VMs have a name and are specified by a path and the binary to be executed executors: @@ -831,6 +831,22 @@ executors: path: . executable: som args: "-at -TF -atcfg=mt:mp:pc " + SOMns-interp-asts: + path: . + executable: som + args: "-asts -astic -G -t1 " + SOMns-graal-asts: + path: . + executable: som + args: "-asts -astic -t1 " + SOMns-interp-asts-mc: + path: . + executable: som + args: "-asts -astmc -G -t1 " + SOMns-graal-asts-mc: + path: . + executable: som + args: "-asts -astmc -t1 " # define the benchmarks to be executed for a re-executable benchmark run experiments: @@ -863,6 +879,13 @@ experiments: - stm-steady - csp-steady + - SOMns-interp-asts: + suites: + - savina-interp + - SOMns-graal-asts: + suites: + - savina-jit + SOMns: description: All benchmarks on SOMns with Graal suites: @@ -881,12 +904,47 @@ experiments: SOMns-Savina: description: Run the Savina Actor benchmarks executions: - - SOMns-interp: - suites: - - savina-interp + # - SOMns-interp: + # suites: + # - savina-interp - SOMns-graal: - suites: - - savina-jit + suites: + - savina-jit + SOMns-ASTS: + description: Run the Savina Actor benchmarks using async stack traces + executions: + # - SOMns-interp-asts: + # suites: + # - savina-interp + - SOMns-interp-asts-mc: + suites: + - savina-interp + SOMns-ASTS-JIT: + executions: + # - SOMns-graal-asts: + # suites: + # - savina-jit + - SOMns-graal-asts-mc: + suites: + - savina-jit + + # SOMns-ASTS: + # description: Run the Savina Actor benchmarks using async stack traces + # executions: + # - SOMns-interp-asts + # suites: + # - savina-interp + # - SOMns-interp-asts-mc + # suites: + # - savina-interp + # SOMns-ASTS-JIT: + # executions: + # - SOMns-graal-asts: + # suites: + # - savina-jit + # - SOMns-graal-asts-mc: + # suites: + # - savina-jit SOMns-concurrency-models: suites: diff --git a/core-lib/Actors.ns b/core-lib/Actors.ns index b4c3b9ef70..6ca9e13d55 100644 --- a/core-lib/Actors.ns +++ b/core-lib/Actors.ns @@ -74,13 +74,13 @@ class Actors usingVmMirror: vmMirror usingKernel: kernel = Value ( public ensure: aBlock = ( ^ whenResolved: aBlock onError: aBlock ) public , other = ( - ^ (PromiseGroup for: self), other. + ^ vmMirror actorsChainPromise: self with: other. ) ) public class Resolver = Value ()( (* Object or Value? *) - public resolve: value = ( vmMirror actorsResolve: self with: value isBPResolver: false isBPResolution: false ) - public error: value = ( vmMirror actorsError: self with: value isBPResolver: false isBPResolution: false ) + public resolve: value = ( vmMirror actorsResolve: self with: value ) + public error: value = ( vmMirror actorsError: self with: value ) ) class Pair with: promise and: resolver = ( diff --git a/core-lib/Benchmarks/Savina.ns b/core-lib/Benchmarks/Savina.ns index b6c0aa20ef..708d8a0e17 100644 --- a/core-lib/Benchmarks/Savina.ns +++ b/core-lib/Benchmarks/Savina.ns @@ -2,7 +2,7 @@ class Savina usingPlatform: platform andHarness: harness = Value ( | private Benchmark = harness Benchmark. private actors = platform actors. private Array = platform kernel Array. - private TransferArray= platform kernel TransferArray. + private TransferArray = platform kernel TransferArray. private Vector = platform kernel Vector. private Dictionary= platform collections Dictionary. private system = platform system. diff --git a/core-lib/Kernel.ns b/core-lib/Kernel.ns index bff84b44a8..fa934b4f84 100644 --- a/core-lib/Kernel.ns +++ b/core-lib/Kernel.ns @@ -26,9 +26,11 @@ class Kernel vmMirror: vmMirror = Object <: Value ( private class Top = ()() public class Thing = Top ()( + protected error: msg = ( vmMirror printNewline: 'ERROR: ' + msg. vmMirror markTurnErroneous: nil. + vmMirror printStackTrace: nil. (* enables printing the stack trace when an error is thrown *) vmMirror exit: 1 ) @@ -74,6 +76,8 @@ class Kernel vmMirror: vmMirror = Object <: Value ( public print = ( self asString print ) public println = ( vmMirror printNewline: self asString ) public halt = ( vmMirror halt: self ) + public asyncTrace = (^ vmMirror asyncTrace: self ) + public resetAsyncTrace = (vmMirror resetAsyncTrace: self) public asString = ( ^ 'instance of ' + (vmMirror objClassName: self) ) ) diff --git a/core-lib/TestSuite/AsyncTests/AsyncStackTracesTest.ns b/core-lib/TestSuite/AsyncTests/AsyncStackTracesTest.ns new file mode 100644 index 0000000000..cb03e4dae4 --- /dev/null +++ b/core-lib/TestSuite/AsyncTests/AsyncStackTracesTest.ns @@ -0,0 +1,306 @@ +class AsyncStackTracesTest usingPlatform: platform testFramework: minitest = Value ( +| private TestContext = minitest TestContext. + private AsyncTestContext = minitest AsyncTestContext. + private ClassMirror = platform mirrors ClassMirror. + private Task = platform threading Task. + public actors = platform actors. + private Delay = platform threading Delay. + private ValueArray = platform kernel ValueArray. + | +)( public class PingPong new: numPings = Value ( + | private NumPings = numPings. + | + )( + public class Ping new: cnt with: pong = ( + | private pingsLeft ::= cnt. + private pong = pong. + | + ) ( + public start = ( + pong <-: ping: self. + pingsLeft:: pingsLeft - 1. + ) + + public ping = ( + pong <-: ping: self. + pingsLeft:: pingsLeft - 1. + ) + + public pong: sender = ( + pingsLeft > 0 + ifTrue: [ self <-: ping ] + ifFalse: [ pong <-: stop ]. + ) + ) + + public class Pong new: completionRes = ( + | private pongCount ::= 0. + private completionRes = completionRes. + public asyncStack ::= 0. + | + ) ( + public ping: sender = ( + 'PONG PINGING' println. + asyncStack:: 1 asyncTrace. + 'PONG PINGED' println. + sender <-: pong: self. + pongCount:: pongCount + 1. + ) + + public stop = ( + 'STOPPING' println. + completionRes resolve: asyncStack. + ) + ) + + public pingPong = ( + |completionPP ping pong | + completionPP:: actors createPromisePair. + pong:: (actors createActorFromValue: Pong) <-: new: completionPP resolver. + ping:: (actors createActorFromValue: Ping) <-: new: NumPings with: pong. + ping <-: start. + ^ completionPP promise + ) + ) + + + public class AsyncStackFrame new: asyncString = ( + (* This class represents an async stack frame *) + | + private asyncStrings = self splitAsyncString: asyncString. + public actorID = asyncStrings at: 1. + public methodSignature = asyncStrings at: 2. + public fileName = asyncStrings at: 3. + public fileLine = (asyncStrings size > 3) ifTrue: [ + asyncStrings at: 4. + ] ifFalse: ''. + + | + + )( + + public splitAsyncString: aString = ( + || + ^ (aString split: ',') collect: [:string | (string beginsWith: ' ') ifTrue: [string substringFrom: 2 to: string length ] ifFalse: [string] ]. + + ) + + ) + + private class AsyncTestBase = AsyncTestContext () ( + (* This class represent the base class for async stack trace tests, shadowing + the asyncTrace primitive to initialize AsyncStackFrame*) + public isAsync = ( ^ true ) + + public asyncTrace = ( + ^ (1 asyncTrace collect: [:e | (e beginsWith: 'PromiseGroup ') ifTrue: [ AsyncStackFrame new: ' , Parallel Call Stack , , ' ] ifFalse: [AsyncStackFrame new: e]]) copyFrom: 3. + ) + + public asyncTrace: aTrace = ( + ^ (aTrace collect: [:e | AsyncStackFrame new: e]) copyFrom: 2. + ) + ) + + + public class BasicAsyncTracesTests = AsyncTestBase () ( + + public testRetrievingAsyncStackTraceReturnsArrayOfTraces = ( + (* IF I use reject: [... == Class] I get a True doesNotUnderstand: == + but it works with select: [... ~= Class] *) + | trace asyncTrace res | + trace:: self asyncTrace. + res:: (trace select: [:e | ((ClassMirror reflecting: e) classObject) ~= AsyncStackFrame ]). + self assert: res isEmpty. + ) + public testRetrievingAsyncStackTraceHasRightTopFrame = ( + | trace asyncTrace | + trace:: self asyncTrace. + self assert:(trace at: 1) methodSignature equals: 'BasicAsyncTracesTests>>#testRetrievingAsyncStackTraceHasRightTopFrame' + ) + + ) : ( TEST_CONTEXT = () ) + + + public class ForkJoinAsyncStackTraceTest = AsyncTestBase () ( + private calledMethod = ( + ^ self asyncTrace) + public testForkedAsyncStackTraceHasCorrectTopFrame = ( + | task trace asyncTrace result | + task:: Task spawn: [ trace:: calledMethod ]. + result:: task join. + self assert:(trace at: 1) methodSignature equals: 'ForkJoinAsyncStackTraceTest>>#calledMethod' + ) + + public testForkedAsyncStackTraceHasCallToSpawn = ( + | task trace asyncTrace result | + task:: Task spawn: [ trace:: calledMethod ]. + task join. + result:: trace collect: [:e | e methodSignature]. + self assert: (result contains: 'Task>>#spawnPrim:') + ) + + public testForkedAsyncStackTraceHasCallToThisMethod = ( + | task trace asyncTrace result | + task:: Task spawn: [ trace:: calledMethod ]. + task join. + result:: trace collect: [:e | e methodSignature]. + self assert: (result contains: 'ForkJoinAsyncStackTraceTest>>#testForkedAsyncStackTraceHasCallToThisMethod') + ) + ) : ( TEST_CONTEXT = () ) + + public class ActorAsyncStackTraceTest = AsyncTestBase ( + ) ( + private calledMethod = (^ self asyncTrace) + + public testAsyncPongAsyncStackTraceHasCorrectTopFrame = ( + | promise result pp toAssert| + pp:: PingPong new: 1. + promise:: pp pingPong. + toAssert:: promise whenResolved: [:res | + |firstFrame| + firstFrame:: (asyncTrace: res) at: 1. + firstFrame methodSignature. + ]. + ^ assert: toAssert resolvedWith: 'Pong>>#ping:' + ) + + public testAsyncPongAsyncStackTraceHasCallOfPingStart = ( + | promise result pp resolved toAssert| + pp:: PingPong new: 1. + promise:: pp pingPong. + toAssert:: promise whenResolved: [:res | + |collectedStack correctFrameIndex result| + collectedStack:: (asyncTrace: res) collect: [:e | e methodSignature]. + correctFrameIndex:: collectedStack indexOf: 'Ping>>#start'. + correctFrameIndex ifNotNil: [ + result:: (collectedStack at: correctFrameIndex) + ]. + result + ]. + ^ assert: toAssert resolvedWith: 'Ping>>#start' + ) + + public testAsyncPongAsyncStackTraceHasCallToThisMethod = ( + | promise result pp resolved toAssert| + pp:: PingPong new: 1. + promise:: pp pingPong. + toAssert:: promise whenResolved: [:res | + |collectedStack correctFrameIndex result| + collectedStack:: (asyncTrace: res) collect: [:e | e methodSignature]. + correctFrameIndex:: collectedStack indexOf: 'ActorAsyncStackTraceTest>>#testAsyncPongAsyncStackTraceHasCallToThisMethod'. + correctFrameIndex ~= nil. + ]. + ^ assert: toAssert resolvedWith: true + ) + + public testAsyncPong2AsyncStackTraceHasTwoCallsToPongPing = ( + | promise result pp resolved toAssert| + pp:: PingPong new: 2. + promise:: pp pingPong. + toAssert:: promise whenResolved: [:res | + |collectedStack correctFrameIndex result| + collectedStack:: (asyncTrace: res) collect: [:e | e methodSignature]. + correctFrameIndex:: collectedStack select: [:e | e = 'Pong>>#ping:']. + correctFrameIndex size = 2. + ]. + ^ assert: toAssert resolvedWith: true + ) + + + public testAsyncPong2AsyncStackTraceHasCallOfPingStart = ( + | promise result pp resolved toAssert| + pp:: PingPong new: 2. + promise:: pp pingPong. + toAssert:: promise whenResolved: [:res | + |collectedStack correctFrameIndex result| + collectedStack:: (asyncTrace: res) collect: [:e | e methodSignature]. + correctFrameIndex:: collectedStack indexOf: 'Ping>>#start'. + correctFrameIndex ~= nil. + ]. + ^ assert: toAssert resolvedWith: true + ) + + public testAsyncPong2AsyncStackTraceHasCallToStop = ( + | promise result pp resolved toAssert| + pp:: PingPong new: 2. + promise:: pp pingPong. + toAssert:: promise whenResolved: [:res | + |collectedStack correctFrameIndex result| + collectedStack:: (asyncTrace) collect: [:e | e methodSignature]. + correctFrameIndex:: collectedStack indexOf: '#stop'. + correctFrameIndex ~= nil. + ]. + ^ assert: toAssert resolvedWith: true + ) + + public cleanUp = ( + 1 resetAsyncTrace. + ) + + + ) : ( TEST_CONTEXT = () ) + + public class PromisesAsyncTests = AsyncTestBase () ( + + class Promiser = ( + | | + )( + + public a: aResolver = ( + aResolver resolve: 'A'. + ) + + public b: aResolver = ( + aResolver resolve: 'B'. + ) + + + + ) + + public testAsyncPromises = ( + |pp1 pp2 promiser trace| + pp1:: actors createPromisePair. + pp2:: actors createPromisePair. + promiser:: Promiser new. + pp1 promise whenResolved: [:v | trace:: self asyncTrace. + v.]. + promiser a: pp1 resolver. + ^self assert: pp1 promise resolvedWith: 'A'. + ) + + public testAsyncPromisesChainWhenResolved = ( + |pp1 p promiser trace| + pp1:: actors createPromisePair. + promiser:: Promiser new. + p:: pp1 promise whenResolved: [:v | v ]. + p whenResolved: [:v | + trace:: self asyncTrace. + ]. + promiser a: pp1 resolver. + ^self assert: p resolvedWith: 'A'. + ) + + + public testAsyncPromisesChainWhenResolvedOnPromiseGroupIncludesBothCallsToPromiser = ( + |pp1 pp2 p promiser trace resultPromise| + pp1:: actors createPromisePair. + pp2:: actors createPromisePair. + promiser:: Promiser new. + p:: pp1 promise , pp2 promise. + resultPromise:: p whenResolved: [:v | + self asyncTrace.]. + promiser b: pp2 resolver. + Task spawn: [promiser a: pp1 resolver.]. + ^self assert: resultPromise resolvedWithBlock: [:trace | |collectedStack first second| + collectedStack:: trace collect: [:e | e methodSignature]. + first:: (collectedStack indexOf: 'Promiser>>#a:'). + second:: (collectedStack indexOf: 'Promiser>>#b:'). + (first ~= nil) and: ((second) ~= nil). + ]. + ) + + ) : ( TEST_CONTEXT = () ) + + +) diff --git a/core-lib/TestSuite/Minitest.ns b/core-lib/TestSuite/Minitest.ns index 8398c2d443..a4a37c90fe 100644 --- a/core-lib/TestSuite/Minitest.ns +++ b/core-lib/TestSuite/Minitest.ns @@ -441,12 +441,12 @@ OTHER DEALINGS IN THE SOFTWARE. *) exClass:: (ClassMirror reflecting: e) classObject. exClass == TestFailureException ifTrue: [ promisePair resolve: (testContextInstance - ifNil: [ TestFailure case: self description: ex messageText] - ifNotNil: [:it | it createFailureResultFor: self description: ex messageText]) + ifNil: [ TestFailure case: self description: e messageText] + ifNotNil: [:it | it createFailureResultFor: self description: e messageText]) ] ifFalse: [ promisePair resolve: (testContextInstance - ifNil: [ TestError case: self exception: ex ] - ifNotNil: [:it | it createErrorResultFor: self exception: ex ]) + ifNil: [ TestError case: self exception: e ] + ifNotNil: [:it | it createErrorResultFor: self exception: e ]) ] ] ] on: TestFailureException @@ -772,6 +772,14 @@ OTHER DEALINGS IN THE SOFTWARE. *) failWithMessage: 'Promise broken with error: ' + actualResolution asString ] ) + assert: promise resolvedWithBlock: aBlock = ( + ^ promise + whenResolved: [:actualResolution | + assert: [aBlock value: actualResolution] ] + onError: [:actualResolution | + failWithMessage: 'Promise broken with error: ' + actualResolution asString ] + ) + assert: promise erroredWith: expectedErrorClass = ( (* | state actualResolution | state:: #unresolved. diff --git a/libs/truffle b/libs/truffle index acb60536e8..c3055fe4e3 160000 --- a/libs/truffle +++ b/libs/truffle @@ -1 +1 @@ -Subproject commit acb60536e81fae64d6409ca07c959bde4e402bd5 +Subproject commit c3055fe4e35b2dc7507558ae735b9303d061979a diff --git a/som b/som index 9d305b71fc..0c685d63cb 100755 --- a/som +++ b/som @@ -108,6 +108,13 @@ tools.add_argument('--java-coverage', help='determine Java code coverage and sto tools.add_argument('-kt', '--kompos-tracing', help='enable tracing of actor operations', dest='kompos_tracing', action='store_true', default=False) +tools.add_argument('-asts', '--async_stack_traces_structure', help='enable async stack traces in debugging through a separate structure, default value is false', + dest='async_stack_traces_structure', action='store_true', default=False) +tools.add_argument('-astmc', '--async_stack_traces_method_cache', help='enable async stack traces in debugging through a separate structure and add a per method backpointer cache, default value is false', + dest='async_stack_traces_method_cache', action='store_true', default=False) +tools.add_argument('-astic', '--async_stack_traces_inline_cache', help='enable async stack traces in debugging through a separate structure and add a per send site cache, default value is false', + dest='async_stack_traces_inline_cache', action='store_true', default=False) + parser.add_argument('-o', '--only', help='only compile give methods, comma separated list', dest='only_compile', default=None) parser.add_argument('-A', '--no-assert', help='execute with assertions disabled', @@ -223,6 +230,7 @@ else: classpath = (BASE_DIR + '/build/classes:' + BASE_DIR + '/libs/black-diamonds/build/classes:' + + BASE_DIR + '/libs/Java-WebSocket-1.5.1.jar:' + BASE_DIR + '/libs/somns-deps.jar:' + BASE_DIR + '/libs/affinity.jar:' + BASE_DIR + '/libs/slf4j-api.jar:' @@ -331,6 +339,13 @@ if args.truffle_profile: if args.coverage: SOM_ARGS += ['--coverage', args.coverage] +if args.async_stack_traces_structure: + flags += ['-Dsom.actorAsyncStackTraceStructure=true'] +if args.async_stack_traces_method_cache: + flags += ['-Dsom.actorAsyncStackTraceMethodCache=true'] +if args.async_stack_traces_inline_cache: + flags += ['-Dsom.actorAsyncStackTraceInlineCache=true'] + if args.java_coverage: flags += ['-javaagent:' + BASE_DIR + '/libs/jacoco/lib/jacocoagent.jar=inclbootstrapclasses=true,destfile=' + args.java_coverage] @@ -446,7 +461,7 @@ if args.use_pinning: bit_mask = 0 for c in range(core_set[0], core_set[1] + 1): bit_mask += 1 << c - flags += ['-Daffinity.reserved=' + hex(bit_mask).lstrip("0x")] + flags += ['-Daffinity.reserved=' + hex(bit_mask).lstrip("0x"), '-Dorg.slf4j.simpleLogger.defaultLogLevel=off'] else: flags += ['-Dsom.usePinning=false'] flags += ['-Dorg.slf4j.simpleLogger.defaultLogLevel=off'] diff --git a/src/som/Launcher.java b/src/som/Launcher.java index 037305bb1a..8d4db42903 100644 --- a/src/som/Launcher.java +++ b/src/som/Launcher.java @@ -9,6 +9,7 @@ import som.interpreter.SomLanguage; import som.vm.VmSettings; +import tools.concurrency.TracingActors; import tools.concurrency.TracingBackend; import tools.parser.KomposTraceParser; import tools.snapshot.SnapshotBackend; @@ -39,6 +40,11 @@ public static void main(final String[] args) { Value result = context.eval(START); exitCode = result.as(Integer.class); } finally { + if (VmSettings.TRUFFLE_DEBUGGER_ENABLED) { + // Stop execution for all suspended actors if any + TracingActors.TracingActor.stopActorsIfSuspended(); + } + context.eval(SHUTDOWN); context.close(); finalizeExecution(exitCode); @@ -57,7 +63,11 @@ private static void finalizeExecution(final int exitCode) { // Note: Kompos Trace is parsed right after writing it // to produce the list of messages on the erroneous path. // Could be done at the beginning of assisted debugging. - if (VmSettings.KOMPOS_TRACING) { + + // Note2: added VmSettings.ASSISTED_DEBUGGING flag because at the moment the parser + // does not works correctly with this implementation. + // TODO check the assertion error KomposTraceParser 220 + if (VmSettings.KOMPOS_TRACING && VmSettings.ASSISTED_DEBUGGING) { KomposTraceParser tp = new KomposTraceParser(); tp.createStackTraceFile(VmSettings.TRACE_FILE); } diff --git a/src/som/VM.java b/src/som/VM.java index bed3d113e5..55a303389c 100644 --- a/src/som/VM.java +++ b/src/som/VM.java @@ -50,9 +50,10 @@ import som.vmobjects.SObjectWithClass.SObjectWithoutFields; import som.vmobjects.SSymbol; import tools.concurrency.KomposTrace; +import tools.concurrency.TracingActors; import tools.concurrency.TracingBackend; import tools.debugger.WebDebugger; -import tools.debugger.session.Breakpoints; +import tools.debugger.breakpoints.Breakpoints; import tools.dym.DynamicMetrics; import tools.replay.TraceParser; import tools.snapshot.SnapshotBackend; @@ -335,6 +336,7 @@ public void initalize(final SomLanguage lang) throws IOException { } if (VmSettings.KOMPOS_TRACING) { KomposTrace.recordMainActor(mainActor, objectSystem); + TracingActors.TracingActor.saveActor(mainActor); } language = lang; @@ -377,8 +379,7 @@ public void setupInstruments(final Env env) { } if (VmSettings.TRUFFLE_DEBUGGER_ENABLED) { - assert options.webDebuggerEnabled - : "If debugging is enabled, we currently expect the web debugger to be used."; + assert options.webDebuggerEnabled : "If debugging is enabled, we currently expect the web debugger to be used."; Debugger debugger = Debugger.find(env); webDebugger = WebDebugger.find(env); @@ -405,8 +406,7 @@ public void setupInstruments(final Env env) { } if (VmSettings.TRACK_SNAPSHOT_ENTITIES) { - assert !options.siCandidateIdentifierEnabled - : "Currently, CandidateIdentifer and Snapshots are not compatible"; + assert !options.siCandidateIdentifierEnabled : "Currently, CandidateIdentifer and Snapshots are not compatible"; structuralProbe = SnapshotBackend.getProbe(); } } diff --git a/src/som/compiler/MixinDefinition.java b/src/som/compiler/MixinDefinition.java index 539cc4ae7a..b23d304e58 100644 --- a/src/som/compiler/MixinDefinition.java +++ b/src/som/compiler/MixinDefinition.java @@ -17,6 +17,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.nodes.ExplodeLoop; @@ -32,6 +33,7 @@ import som.interpreter.LexicalScope.MethodScope; import som.interpreter.LexicalScope.MixinScope; import som.interpreter.Method; +import som.interpreter.SArguments; import som.interpreter.SNodeFactory; import som.interpreter.SomLanguage; import som.interpreter.nodes.ExceptionSignalingNode; @@ -534,16 +536,27 @@ public SClass instantiateModuleClass() { VM.callerNeedsToBeOptimized( "only meant for code loading, which is supposed to be on the slowpath"); CallTarget callTarget = superclassMixinResolution.getCallTarget(); - SClass superClass = (SClass) callTarget.call(Nil.nilObject); - SClass classObject = instantiateClass(Nil.nilObject, superClass); + // SClass superClass = (SClass) callTarget.call(Nil.nilObject); + // SClass classObject = instantiateClass(Nil.nilObject, superClass); + SClass superClass; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // Since this is outside of the main stacks, we create a separate top shadow stack entry + // to deal with async errors. + superClass = + (SClass) callTarget.call(Nil.nilObject, + SArguments.instantiateTopShadowStackEntry(null)); + } else { + superClass = (SClass) callTarget.call(Nil.nilObject, null); + } + SClass classObject = instantiateClass(null, Nil.nilObject, superClass); return classObject; } - public SClass instantiateClass(final SObjectWithClass outer, + public SClass instantiateClass(final VirtualFrame frame, final SObjectWithClass outer, final Object superclassAndMixins) { ClassFactory factory = createClassFactory(superclassAndMixins, false, false, false, UninitializedObjectSerializationNodeFactory.getInstance()); - return ClassInstantiationNode.instantiate(outer, factory, notAValue, + return ClassInstantiationNode.instantiate(frame, outer, factory, notAValue, cannotBeValues); } @@ -735,8 +748,11 @@ public Object invoke(final IndirectCallNode call, final Object[] arguments) { if (TruffleOptions.AOT) { CompilerDirectives.transferToInterpreter(); } - - assert arguments.length == 1; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + assert arguments.length == 2; + } else { + assert arguments.length == 1; + } SObject rcvr = (SObject) arguments[0]; Object result = rcvr.readSlot(this); assert result != null; @@ -750,10 +766,21 @@ public Object invoke(final IndirectCallNode call, final Object[] arguments) { if (result != Nil.nilObject) { return result; } + Object superclassAndMixins; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + superclassAndMixins = mixinDefinition.getSuperclassAndMixinResolutionInvokable() + .getCallTarget().call(rcvr, + SArguments.instantiateTopShadowStackEntry( + mixinDefinition.getSuperclassAndMixinResolutionInvokable())); + } else { + superclassAndMixins = mixinDefinition.getSuperclassAndMixinResolutionInvokable() + .getCallTarget().call(rcvr); + } // ok, now it is for sure not initialized yet, instantiate class - Object superclassAndMixins = mixinDefinition.getSuperclassAndMixinResolutionInvokable() - .getCallTarget().call(rcvr); - SClass clazz = mixinDefinition.instantiateClass(rcvr, superclassAndMixins); + // Object superclassAndMixins = + // mixinDefinition.getSuperclassAndMixinResolutionInvokable() + // .getCallTarget().call(rcvr); + SClass clazz = mixinDefinition.instantiateClass(null, rcvr, superclassAndMixins); rcvr.writeSlot(this, clazz); return clazz; } diff --git a/src/som/compiler/Variable.java b/src/som/compiler/Variable.java index 06000fe7ae..335bee4b03 100644 --- a/src/som/compiler/Variable.java +++ b/src/som/compiler/Variable.java @@ -90,8 +90,7 @@ public boolean equals(final Object o) { return true; } assert source == null || !source.equals( - var.source) - : "Why are there multiple objects for this source section? might need to fix comparison above"; + var.source) : "Why are there multiple objects for this source section? might need to fix comparison above"; return false; } diff --git a/src/som/instrumentation/CountingDirectCallNode.java b/src/som/instrumentation/CountingDirectCallNode.java index a57014b24c..30b8e0cb65 100644 --- a/src/som/instrumentation/CountingDirectCallNode.java +++ b/src/som/instrumentation/CountingDirectCallNode.java @@ -15,7 +15,7 @@ public final class CountingDirectCallNode extends DirectCallNode { private final AtomicInteger counter; public CountingDirectCallNode(final DirectCallNode callNode) { - super(null); + super(callNode.getCallTarget()); this.callNode = callNode; this.counter = new AtomicInteger(); } diff --git a/src/som/interpreter/Invokable.java b/src/som/interpreter/Invokable.java index 726c3388d0..2f8a560e85 100644 --- a/src/som/interpreter/Invokable.java +++ b/src/som/interpreter/Invokable.java @@ -45,6 +45,10 @@ public final boolean isAtomic() { return isAtomic; } + public ExpressionNode getBodyNode() { + return expressionOrSequence; + } + @Override public final Object execute(final VirtualFrame frame) { return expressionOrSequence.executeGeneric(frame); diff --git a/src/som/interpreter/LexicalScope.java b/src/som/interpreter/LexicalScope.java index b1e9bfef5b..23cfdb7275 100644 --- a/src/som/interpreter/LexicalScope.java +++ b/src/som/interpreter/LexicalScope.java @@ -434,8 +434,7 @@ public String toString() { @Override public MixinScope getMixinScope() { - assert outerScope != null - : "Should not be possible, because we do not support top-level methods"; + assert outerScope != null : "Should not be possible, because we do not support top-level methods"; return outerScope.getMixinScope(); } @@ -460,15 +459,13 @@ public MethodScope getOuterMethod() { @Override public boolean lookupSlotOrClass(final SSymbol selector, final List results) { - assert outerScope != null - : "Should not be possible, because we do not support top-level methods, except for superclass resolution"; + assert outerScope != null : "Should not be possible, because we do not support top-level methods, except for superclass resolution"; // this traversal concerns only the enclosing objects, not the activations return outerScope.lookupSlotOrClass(selector, results); } public MethodScope getEmbeddedScope(final SourceSection source) { - assert embeddedScopes != null - : "Something is wrong, trying to get embedded scope for leaf method, except for superclass resolution"; + assert embeddedScopes != null : "Something is wrong, trying to get embedded scope for leaf method, except for superclass resolution"; for (MethodScope s : embeddedScopes) { if (s.method.getSourceSection().equals(source)) { return s; diff --git a/src/som/interpreter/Method.java b/src/som/interpreter/Method.java index 7fc31ddffb..b385d847ae 100644 --- a/src/som/interpreter/Method.java +++ b/src/som/interpreter/Method.java @@ -32,6 +32,7 @@ import som.interpreter.LexicalScope.MethodScope; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.SOMNode; +import som.interpreter.nodes.dispatch.BackCacheCallNode; import som.vmobjects.SInvokable; @@ -41,6 +42,10 @@ public final class Method extends Invokable { private final SourceSection[] definition; private final boolean block; + // Currently only accessed when iterating the stack + // Thus, this doesn't need to be compilation final + private BackCacheCallNode uniqueCaller; + public Method(final String name, final SourceSection sourceSection, final SourceSection[] definition, final ExpressionNode expressions, @@ -72,11 +77,24 @@ public boolean equals(final Object o) { } assert !getSourceSection().equals( - m.getSourceSection()) - : "If that triggers, something with the source sections is wrong."; + m.getSourceSection()) : "If that triggers, something with the source sections is wrong."; return false; } + public void setNewCaller(final BackCacheCallNode caller) { + if (uniqueCaller == null) { + uniqueCaller = caller; + caller.makeUniqueCaller(); + } else { + uniqueCaller.makeMultipleCaller(); + caller.makeMultipleCaller(); + } + } + + public BackCacheCallNode getUniqueCaller() { + return uniqueCaller; + } + public SourceSection[] getDefinition() { return definition; } diff --git a/src/som/interpreter/SArguments.java b/src/som/interpreter/SArguments.java index bab650d213..2eb676c3c4 100644 --- a/src/som/interpreter/SArguments.java +++ b/src/som/interpreter/SArguments.java @@ -1,12 +1,27 @@ package som.interpreter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import som.interpreter.nodes.ExpressionNode; import som.primitives.SizeAndLengthPrim; import som.primitives.arrays.AtPrim; +import som.vm.VmSettings; import som.vm.constants.Classes; import som.vmobjects.SArray; import som.vmobjects.SArray.SImmutableArray; +import som.vmobjects.SBlock; +import tools.debugger.asyncstacktraces.ShadowStackEntry; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; public final class SArguments { @@ -25,37 +40,302 @@ public static Object rcvr(final Frame frame) { return arg(frame, RCVR_IDX); } + public static Object[] convertToArgumentArray(final Object[] args) { + int argLength = args.length; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + argLength++; + } else { + return args; + } + + Object[] array = Arrays.copyOf(args, argLength); + array[argLength - 1] = SArguments.instantiateTopShadowStackEntry(null); + return array; + } + + public static Object[] allocateArgumentsArray(final ExpressionNode[] argumentNodes) { + int argLength = argumentNodes.length; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + argLength++; + } + return new Object[argLength]; + } + /** * Create a new array from an SArguments array that contains only the true * arguments and excludes the receiver. This is used for instance for * #doesNotUnderstand (#dnu) */ public static SImmutableArray getArgumentsWithoutReceiver(final Object[] arguments) { - if (arguments.length == 1) { - return new SImmutableArray(0, Classes.valueArrayClass); + // if (arguments.length == 1) { + // return new SImmutableArray(0, Classes.valueArrayClass); + // } + // + // Object[] argsArr = getPlainArgumentWithoutReceiver(arguments); + // return new SImmutableArray(argsArr, Classes.valueArrayClass); + + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + if (arguments.length == 2) { + assert arguments[1] instanceof ShadowStackEntry; + return new SImmutableArray(0, Classes.valueArrayClass); + } + + Object[] argsArr = getPlainArgumentWithoutReceiver(arguments); + return new SImmutableArray(argsArr, Classes.valueArrayClass); + + } else { + if (arguments.length == 1) { + return new SImmutableArray(0, Classes.valueArrayClass); + } + + Object[] argsArr = getPlainArgumentWithoutReceiver(arguments); + return new SImmutableArray(argsArr, Classes.valueArrayClass); } + } - Object[] argsArr = getPlainArgumentWithoutReceiver(arguments); - return new SImmutableArray(argsArr, Classes.valueArrayClass); + public static Object[] getPlainArguments(final Object o, final Object[] objects) { + Object[] allArgs; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + allArgs = new Object[objects.length + 2]; + } else { + allArgs = new Object[objects.length + 1]; + } + allArgs[0] = o; + for (int i = 0; i < objects.length; i++) { + allArgs[i + 1] = objects[i]; + } + return allArgs; } + /** + * Create a new array and copy inside the arguments without the receiver. + * Used for FFI calls and DNUs. + */ public static Object[] getPlainArgumentWithoutReceiver(final Object[] arguments) { int rcvrIdx = 0; // the code and magic numbers below are based on the following assumption assert RCVR_IDX == rcvrIdx; assert arguments.length >= 1; // <- that's the receiver - Object[] argsArr = new Object[arguments.length - 1]; + // Object[] argsArr = new Object[arguments.length - 1]; + + int argsSize = arguments.length - 1; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + argsSize--; + } + Object[] argsArr = new Object[argsSize]; System.arraycopy(arguments, 1, argsArr, 0, argsArr.length); return argsArr; } + // public static Object[] getPlainArgumentsWithReceiver(final Object receiver, + // final SArray args, final SizeAndLengthPrim size, final AtPrim at) { + // Object[] result = new Object[(int) (size.executeEvaluated(args) + 1)]; + // result[0] = receiver; + // for (int i = 1; i < result.length; i++) { + // result[i] = at.executeEvaluated(null, args, (long) i); + // } + // return result; + // } + public static Object[] getPlainArgumentsWithReceiver(final Object receiver, - final SArray args, final SizeAndLengthPrim size, final AtPrim at) { - Object[] result = new Object[(int) (size.executeEvaluated(args) + 1)]; + final SArray args, final SizeAndLengthPrim size, final AtPrim at, + final ExpressionNode expression, + final ShadowStackEntryLoad entryLoad, + final VirtualFrame frame) { + int argSize = (int) (size.executeEvaluated(args) + 1); + int defaultArgSize = argSize; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + argSize++; + } + + Object[] result = new Object[argSize]; result[0] = receiver; - for (int i = 1; i < result.length; i++) { + for (int i = 1; i < defaultArgSize; i++) { result[i] = at.executeEvaluated(null, args, (long) i); } + + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + entryLoad.loadShadowStackEntry(result, expression, frame, false); + } return result; } + + public static Object[] getPlainXArgumentsWithReceiver(final ExpressionNode expression, + final ShadowStackEntryLoad entryLoad, + final VirtualFrame frame, + final Object... rcvrAndArgs) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + Object[] arguments = new Object[rcvrAndArgs.length + 1]; + for (int i = 0; i < rcvrAndArgs.length; i++) { + arguments[i] = rcvrAndArgs[i]; + } + entryLoad.loadShadowStackEntry(arguments, expression, frame, false); + return arguments; + } else { + return rcvrAndArgs; + } + } + + public static Object[] getPlainArguments(final Object[] args) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + Object[] newArgs = new Object[args.length + 1]; + for (int i = 0; i < args.length; i++) { + newArgs[i] = args[i]; + } + return newArgs; + } else { + return args; + } + } + + public static Object[] getPromiseCallbackArgumentArray(final SBlock callback) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + return new Object[] {callback, null, null}; + } else { + return new Object[] {callback, null}; + } + } + + public static void setShadowStackEntryWithCache(final Object[] arguments, + final Node expression, + final ShadowStackEntryLoad entryLoad, + final VirtualFrame frame, + final boolean async) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + entryLoad.loadShadowStackEntry(arguments, expression, frame, async); + } + } + + public static ShadowStackEntry instantiateTopShadowStackEntry(final Node expr) { + return ShadowStackEntry.createTop(expr); + } + + public static ShadowStackEntry instantiateShadowStackEntry(final ShadowStackEntry previous, + final Node expr, final boolean async) { + CompilerAsserts.partialEvaluationConstant(async); +// if (async) { +// return ShadowStackEntry.createAtAsyncSend(previous, expr); +// } else { + return ShadowStackEntry.create(previous, expr); + //} + } + + public static ShadowStackEntry getShadowStackEntry(final VirtualFrame frame) { + if (!VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + return null; + } + + Object[] args = frame.getArguments(); + return getShadowStackEntry(args); + } + + public static ShadowStackEntry getShadowStackEntry(final Object[] args) { + assert VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE; + + Object maybeShadowStack = args[args.length - 1]; + assert maybeShadowStack instanceof ShadowStackEntry || maybeShadowStack == null; + + if (maybeShadowStack == null) { + return null; + } + + return (ShadowStackEntry) maybeShadowStack; + } + + public static void setShadowStackEntry(final Object[] args, final ShadowStackEntry entry) { + assert args[args.length - 1] == null : "Assume shadow stack entry is not already set."; + args[args.length - 1] = entry; + } + + public static void updateShadowStackEntry(final Object[] args, + final ShadowStackEntry entry) { + // assert args[args.length - 1] instanceof ShadowStackEntry : "Assume shadow stack entry is + // set"; + // assert entry.getPreviousShadowStackEntry() == (args[args.length -1]); + args[args.length - 1] = entry; + } + + public static int getLengthWithoutShadowStack(final Object[] arguments) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + return arguments.length - 1; + } else { + return arguments.length; + } + } + + // set the resolution of the promise for the registered callback or message sent to a promise + public static void saveCausalEntryForPromise(final Object previousPromiseStack, + final Object currentPromiseStack) { + assert previousPromiseStack != null && previousPromiseStack instanceof ShadowStackEntry; + assert currentPromiseStack != null && currentPromiseStack instanceof ShadowStackEntry; + ((ShadowStackEntry) currentPromiseStack).setPreviousShadowStackEntry( + (ShadowStackEntry) previousPromiseStack); + } + + private static Map previousPromiseInGroupByActor = new HashMap<>(); + + @TruffleBoundary + public static void saveCausalEntryForPromiseGroup(final Object previousPromiseStack, + final Object callbackPromiseStack, final long actorId) { + if (previousPromiseInGroupByActor != null + && previousPromiseInGroupByActor.containsKey(actorId)) { + ShadowStackEntry firstPromise = + previousPromiseInGroupByActor.get(actorId).getPreviousShadowStackEntry(); + + // group frames of the previous promise with the frames of the new promise, and then set + // the grouped stack entries for the callback + ShadowStackEntry groupedFrames = + groupFrames(firstPromise, (ShadowStackEntry) previousPromiseStack); + saveCausalEntryForPromise(groupedFrames, callbackPromiseStack); + + } else { + saveCausalEntryForPromise(previousPromiseStack, callbackPromiseStack); + previousPromiseInGroupByActor.put(actorId, (ShadowStackEntry) callbackPromiseStack); + } + } + + // group non repeated frames + private static ShadowStackEntry groupFrames(final ShadowStackEntry firstPromiseStack, + final ShadowStackEntry secondPromiseStack) { + List listSecond = getAllEntries(secondPromiseStack, new ArrayList<>()); + List listFirst = getAllEntries(firstPromiseStack, new ArrayList<>()); + + ShadowStackEntry group = + setNewEntryAtEqualSourceSection(firstPromiseStack, listFirst, listSecond); + + return group; + } + + private static List getAllEntries(final ShadowStackEntry stackEntry, + final List list) { + if (stackEntry == null) { + return list; + } else { + list.add(stackEntry); + getAllEntries(stackEntry.getPreviousShadowStackEntry(), list); + } + return list; + } + + // equal source section corresponds to the turn node, from there on the stack frames are the + // same for both promises stacks + private static ShadowStackEntry setNewEntryAtEqualSourceSection( + final ShadowStackEntry stackEntryToAdd, final List listFirst, + final List listSecond) { + for (ShadowStackEntry entrySecond : listSecond) { + for (ShadowStackEntry entryFirst : listFirst) { + boolean entryFirstNotNull = entryFirst.getPreviousShadowStackEntry() != null + && entryFirst.getPreviousShadowStackEntry().getExpression() != null; + boolean entrySecondNotNull = entrySecond.getPreviousShadowStackEntry() != null + && entrySecond.getPreviousShadowStackEntry().getExpression() != null; + if (entryFirstNotNull && entrySecondNotNull + && entrySecond.getPreviousShadowStackEntry().getSourceSection().equals( + entryFirst.getPreviousShadowStackEntry().getSourceSection())) { + entrySecond.setPreviousShadowStackEntry(stackEntryToAdd); + return listSecond.get(0); // return top entry with the update + } + } + } + return listSecond.get(0); // return top entry + } } diff --git a/src/som/interpreter/SNodeFactory.java b/src/som/interpreter/SNodeFactory.java index c9ee82e506..d09705acc3 100644 --- a/src/som/interpreter/SNodeFactory.java +++ b/src/som/interpreter/SNodeFactory.java @@ -13,6 +13,7 @@ import som.interpreter.actors.EventualSendNode; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.InternalObjectArrayNode; +import som.interpreter.nodes.InternalObjectArrayNode.ArgumentEvaluationNode; import som.interpreter.nodes.MessageSendNode; import som.interpreter.nodes.OuterObjectReadNodeGen; import som.interpreter.nodes.ResolvingImplicitReceiverSend; @@ -59,7 +60,9 @@ public static ExpressionNode createMessageSend(final SSymbol msg, final SomLanguage lang) { if (eventualSend) { return new EventualSendNode(msg, exprs.length, - new InternalObjectArrayNode(exprs).initialize(source), source, sendOperator, lang); + // new InternalObjectArrayNode(exprs).initialize(source), source, sendOperator, + // lang); + new ArgumentEvaluationNode(exprs).initialize(source), source, sendOperator, lang); } else { return MessageSendNode.createMessageSend(msg, exprs, source, lang.getVM()); } diff --git a/src/som/interpreter/SomLanguage.java b/src/som/interpreter/SomLanguage.java index 9a1761bf22..557418b4e1 100644 --- a/src/som/interpreter/SomLanguage.java +++ b/src/som/interpreter/SomLanguage.java @@ -84,7 +84,7 @@ @TruffleLanguage.Registration(id = "SOMns", name = "SOMns", version = "0.6.0", - interactive = false, internal = false, + interactive = true, internal = false, characterMimeTypes = "application/x-newspeak-som-ns") @ProvidedTags({ RootTag.class, StatementTag.class, CallTag.class, ExpressionTag.class, diff --git a/src/som/interpreter/actors/AbstractPromiseResolutionNode.java b/src/som/interpreter/actors/AbstractPromiseResolutionNode.java index f2b5d5b9e5..24cffe4492 100644 --- a/src/som/interpreter/actors/AbstractPromiseResolutionNode.java +++ b/src/som/interpreter/actors/AbstractPromiseResolutionNode.java @@ -3,10 +3,13 @@ import java.util.concurrent.ForkJoinPool; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.dsl.NodeChild; +import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.source.SourceSection; @@ -16,9 +19,13 @@ import som.interpreter.actors.SPromise.SReplayPromise; import som.interpreter.actors.SPromise.SResolver; import som.interpreter.actors.SPromise.STracingPromise; -import som.interpreter.nodes.nary.QuaternaryExpressionNode; +import som.interpreter.nodes.ExpressionNode; +import som.interpreter.nodes.nary.EagerPrimitiveNode; +import som.interpreter.nodes.nary.EagerlySpecializableNode; import som.interpreter.nodes.nary.UnaryExpressionNode; +import som.vm.NotYetImplementedException; import som.vm.VmSettings; +import som.vmobjects.SSymbol; import tools.concurrency.KomposTrace; import tools.concurrency.TracingActivityThread; import tools.replay.ReplayRecord; @@ -27,7 +34,13 @@ @GenerateWrapper -public abstract class AbstractPromiseResolutionNode extends QuaternaryExpressionNode +@NodeChildren({ + @NodeChild(value = "receiver", type = ExpressionNode.class), + @NodeChild(value = "firstArg", type = ExpressionNode.class), + @NodeChild(value = "secondArg", type = ExpressionNode.class), + @NodeChild(value = "thirdArg", type = ExpressionNode.class), + @NodeChild(value = "fourthArg", type = ExpressionNode.class)}) +public abstract class AbstractPromiseResolutionNode extends EagerlySpecializableNode implements WithContext { @CompilationFinal private ForkJoinPool actorPool; @@ -67,8 +80,24 @@ public AbstractPromiseResolutionNode initialize(final SourceSection sourceSectio } public abstract Object executeEvaluated(VirtualFrame frame, - SResolver receiver, Object argument, boolean haltOnResolver, - boolean haltOnResolution); + SResolver receiver, Object argument, Object maybeEntry, + boolean haltOnResolver, boolean haltOnResolution); + + public abstract Object executeEvaluated(VirtualFrame frame, Object receiver, + Object firstArg, Object secondArg, Object thirdArg, Object forth); + + @Override + public final Object doPreEvaluated(final VirtualFrame frame, + final Object[] arguments) { + return executeEvaluated(frame, arguments[0], arguments[1], arguments[2], + arguments[3], arguments[4]); + } + + @Override + public EagerPrimitiveNode wrapInEagerWrapper(final SSymbol selector, + final ExpressionNode[] arguments, final VM vm) { + throw new NotYetImplementedException(); // wasn't needed so far + } @Override public WrapperNode createWrapper(final ProbeNode probe) { @@ -80,8 +109,8 @@ public WrapperNode createWrapper(final ProbeNode probe) { */ @Specialization(guards = {"resolver.getPromise() == result"}) public SResolver selfResolution(final SResolver resolver, - final SPromise result, final boolean haltOnResolver, - final boolean haltOnResolution) { + final SPromise result, final Object maybeEntry, + final boolean haltOnResolver, final boolean haltOnResolution) { return resolver; } @@ -90,9 +119,9 @@ public SResolver selfResolution(final SResolver resolver, */ @Specialization(guards = {"resolver.getPromise() != promiseValue"}) public SResolver chainedPromise(final VirtualFrame frame, - final SResolver resolver, final SPromise promiseValue, + final SResolver resolver, final SPromise promiseValue, final Object maybeEntry, final boolean haltOnResolver, final boolean haltOnResolution) { - chainPromise(resolver, promiseValue, haltOnResolver, haltOnResolution); + chainPromise(frame, resolver, promiseValue, maybeEntry, haltOnResolver, haltOnResolution); return resolver; } @@ -100,9 +129,9 @@ protected static boolean notAPromise(final Object result) { return !(result instanceof SPromise); } - protected void chainPromise(final SResolver resolver, - final SPromise promiseValue, final boolean haltOnResolver, - final boolean haltOnResolution) { + protected void chainPromise(final VirtualFrame frame, final SResolver resolver, + final SPromise promiseValue, final Object maybeEntry, + final boolean haltOnResolver, final boolean haltOnResolution) { assert resolver.assertNotCompleted(); SPromise promiseToBeResolved = resolver.getPromise(); if (VmSettings.KOMPOS_TRACING) { @@ -124,8 +153,8 @@ protected void chainPromise(final SResolver resolver, } if (SPromise.isCompleted(state)) { - resolvePromise(state, resolver, promiseValue.getValueUnsync(), - haltOnResolution); + resolvePromise(state, resolver, promiseValue.getValueUnsync(), maybeEntry, + haltOnResolution, frame, this); } else { synchronized (promiseToBeResolved) { // TODO: is this really deadlock free? if (haltOnResolution || promiseValue.getHaltOnResolution()) { @@ -150,24 +179,29 @@ protected void chainPromise(final SResolver resolver, } protected void resolvePromise(final Resolution type, - final SResolver resolver, final Object result, - final boolean haltOnResolution) { + final SResolver resolver, final Object result, final Object maybeEntry, + final boolean haltOnResolution, final VirtualFrame frame, final Node expression) { + assert actorPool != null : "Did initialize(VM) get called?"; SPromise promise = resolver.getPromise(); Actor current = EventualMessage.getActorCurrentMessageIsExecutionOn(); - resolve(type, wrapper, promise, result, current, actorPool, haltOnResolution, - whenResolvedProfile, tracePromiseResolution, tracePromiseResolutionEnd); + resolve(type, wrapper, promise, result, current, actorPool, maybeEntry, haltOnResolution, + whenResolvedProfile, frame, expression, tracePromiseResolution, + tracePromiseResolutionEnd); } public static void resolve(final Resolution type, final WrapReferenceNode wrapper, final SPromise promise, final Object result, final Actor current, final ForkJoinPool actorPool, + final Object maybeEntry, final boolean haltOnResolution, final ValueProfile whenResolvedProfile, + final VirtualFrame frame, final Node expression, final RecordOneEvent tracePromiseResolution2, final RecordOneEvent tracePromiseResolutionEnd2) { Object wrapped = wrapper.execute(result, promise.owner, current); SResolver.resolveAndTriggerListenersUnsynced(type, result, wrapped, promise, - current, actorPool, haltOnResolution, whenResolvedProfile, tracePromiseResolution2, + current, actorPool, maybeEntry, haltOnResolution, whenResolvedProfile, frame, + expression, tracePromiseResolution2, tracePromiseResolutionEnd2); } } diff --git a/src/som/interpreter/actors/Actor.java b/src/som/interpreter/actors/Actor.java index da5cfd1280..50c3f6ea10 100644 --- a/src/som/interpreter/actors/Actor.java +++ b/src/som/interpreter/actors/Actor.java @@ -139,11 +139,19 @@ private void doSend(final EventualMessage msg, final ForkJoinPool actorPool) { assert msg.getTarget() == this; + assert msg.args[msg.args.length - 1] != null + || !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE; + if (firstMessage == null) { firstMessage = msg; } else { appendToMailbox(msg); } + // so.println("doSend "+msg.getMessageId() + " actor "+this.getId()); + // save messages in the trace when they are received + if (VmSettings.KOMPOS_TRACING) { + TracingActor.saveMessageReceived(this, msg); + } if (!isExecuting) { isExecuting = true; @@ -206,8 +214,7 @@ protected ExecAllMessages(final Actor actor, final VM vm) { @Override public void run() { - assert executorRoot != null - : "Actor system not initalized, call to initializeActorSystem(.) missing?"; + assert executorRoot != null : "Actor system not initalized, call to initializeActorSystem(.) missing?"; executorRoot.call(this); } @@ -246,8 +253,26 @@ private void doRunWithObjectSafepoints(final ActorProcessingThread t) { } while (getCurrentMessagesOrCompleteExecution()) { + saveReceivedMessages(t); processCurrentMessages(t, dbg); } + + if (VmSettings.UNIFORM_TRACING || VmSettings.KOMPOS_TRACING) { + t.swapTracingBufferIfRequestedUnsync(); + } + t.currentlyExecutingActor = null; + } + + private void saveReceivedMessages(final ActorProcessingThread t) { + // save messages appended in mailbox in the trace before execute them + if (VmSettings.KOMPOS_TRACING) { + KomposTrace.actorMessageReception(firstMessage.getMessageId(), t); + if (size > 1) { + for (EventualMessage msg : mailboxExtension) { + KomposTrace.actorMessageReception(msg.getMessageId(), t); + } + } + } } protected void processCurrentMessages(final ActorProcessingThread currentThread, diff --git a/src/som/interpreter/actors/EagerResolvePromiseNode.java b/src/som/interpreter/actors/EagerResolvePromiseNode.java index d826b876e7..331a710df1 100644 --- a/src/som/interpreter/actors/EagerResolvePromiseNode.java +++ b/src/som/interpreter/actors/EagerResolvePromiseNode.java @@ -17,13 +17,13 @@ public abstract class EagerResolvePromiseNode extends BinaryExpressionNode implements WithContext { - @Child protected AbstractPromiseResolutionNode resolve; + @Child protected ResolvePromiseNode resolve; @Override @SuppressWarnings("unchecked") public EagerResolvePromiseNode initialize(final SourceSection sourceSection) { super.initialize(sourceSection); - resolve = ResolvePromiseNodeFactory.create(null, null, null, null); + resolve = ResolvePromiseNodeFactory.create(null, null); resolve.initialize(sourceSection); return this; } @@ -37,6 +37,6 @@ public final EagerResolvePromiseNode initialize(final VM vm) { @Specialization public SResolver resolve(final VirtualFrame frame, final SResolver resolver, final Object value) { - return (SResolver) resolve.executeEvaluated(frame, resolver, value, false, false); + return resolve.executeEvaluated(frame, resolver, value); } } diff --git a/src/som/interpreter/actors/ErrorNode.java b/src/som/interpreter/actors/ErrorNode.java new file mode 100644 index 0000000000..a41c4f604f --- /dev/null +++ b/src/som/interpreter/actors/ErrorNode.java @@ -0,0 +1,52 @@ +package som.interpreter.actors; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; + +import som.interpreter.SArguments; +import som.interpreter.SomLanguage; +import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntry; + + +public abstract class ErrorNode extends AbstractPromiseResolutionNode { + @CompilerDirectives.CompilationFinal boolean initialized = false; + + /** + * Standard error case, when the promise is errored with a value that's not a promise. + */ + @Specialization(guards = {"notAPromise(result)"}) + public SPromise.SResolver standardError(final VirtualFrame frame, + final SPromise.SResolver resolver, final Object result, final Object maybeEntry, + final boolean haltOnResolver, final boolean haltOnResolution) { + + if (!initialized) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + initialized = true; + this.initialize(SomLanguage.getVM(this)); + } + + SPromise promise = resolver.getPromise(); + + if (haltOnResolver || promise.getHaltOnResolver()) { + haltNode.executeEvaluated(frame, result); + } + + ShadowStackEntry resolutionEntry = null; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + ShadowStackEntry entry = SArguments.getShadowStackEntry(frame.getArguments()); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || entry != null; + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation location = + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.ERROR; + resolutionEntry = + ShadowStackEntry.createAtPromiseResolution(entry, this.getParent(), location, + ""); // "error: " + result.toString() + SArguments.saveCausalEntryForPromise(maybeEntry, resolutionEntry); + } + + resolvePromise(SPromise.Resolution.ERRONEOUS, resolver, result, resolutionEntry, + haltOnResolution, frame, this.getParent()); + return resolver; + } +} diff --git a/src/som/interpreter/actors/ErrorPromiseNode.java b/src/som/interpreter/actors/ErrorPromiseNode.java index d5f04c93ca..127390f445 100644 --- a/src/som/interpreter/actors/ErrorPromiseNode.java +++ b/src/som/interpreter/actors/ErrorPromiseNode.java @@ -7,29 +7,28 @@ import bd.primitives.Primitive; import bd.tools.nodes.Operation; -import som.interpreter.actors.SPromise.Resolution; +import som.interpreter.SArguments; import som.interpreter.actors.SPromise.SResolver; +import som.interpreter.nodes.nary.BinaryExpressionNode; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.dym.Tags.ComplexPrimitiveOperation; @GenerateNodeFactory -@Primitive(primitive = "actorsError:with:isBPResolver:isBPResolution:") -public abstract class ErrorPromiseNode extends AbstractPromiseResolutionNode - implements Operation { - /** - * Standard error case, when the promise is errored with a value that's not a promise. - */ - @Specialization(guards = {"notAPromise(result)"}) - public SResolver standardError(final VirtualFrame frame, final SResolver resolver, - final Object result, final boolean haltOnResolver, final boolean haltOnResolution) { - SPromise promise = resolver.getPromise(); +@Primitive(primitive = "actorsError:with:") +public abstract class ErrorPromiseNode extends BinaryExpressionNode implements Operation { + @Child protected ErrorNode errorNode; - if (haltOnResolver || promise.getHaltOnResolver()) { - haltNode.executeEvaluated(frame, result); - } + public ErrorPromiseNode() { + errorNode = ErrorNodeGen.create(null, null, null, null, null); + } - resolvePromise(Resolution.ERRONEOUS, resolver, result, haltOnResolution); - return resolver; + @Specialization + public SResolver standardError(final VirtualFrame frame, final SResolver resolver, + final Object result) { + ShadowStackEntry entry = SArguments.getShadowStackEntry(frame); + return (SResolver) errorNode.executeEvaluated(frame, resolver, result, entry, false, + false); } @Override diff --git a/src/som/interpreter/actors/EventualMessage.java b/src/som/interpreter/actors/EventualMessage.java index 19d45989dc..c74a20f207 100644 --- a/src/som/interpreter/actors/EventualMessage.java +++ b/src/som/interpreter/actors/EventualMessage.java @@ -5,8 +5,11 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.nodes.Node; import som.VM; +import som.interpreter.Method; +import som.interpreter.SArguments; import som.interpreter.actors.Actor.ActorProcessingThread; import som.interpreter.actors.ReceivedMessage.ReceivedCallback; import som.interpreter.actors.SPromise.SResolver; @@ -14,6 +17,7 @@ import som.vmobjects.SBlock; import som.vmobjects.SSymbol; import tools.concurrency.TracingActivityThread; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.parser.KomposTraceParser; import tools.replay.nodes.RecordEventNodes.RecordOneEvent; import tools.snapshot.SnapshotBackend; @@ -49,6 +53,7 @@ public abstract class EventualMessage { protected EventualMessage(final Object[] args, final SResolver resolver, final RootCallTarget onReceive, final boolean haltOnReceive, final boolean haltOnResolver) { + this.args = args; this.resolver = resolver; this.onReceive = onReceive; @@ -137,8 +142,7 @@ protected AbstractDirectMessage(final Actor target, final SSymbol selector, } assert target != null; - assert !(args[0] instanceof SFarReference) - : "needs to be guaranted by call to this constructor"; + assert !(args[0] instanceof SFarReference) : "needs to be guaranted by call to this constructor"; assert !(args[0] instanceof SPromise); } @@ -159,8 +163,7 @@ protected AbstractDirectMessage(final Actor target, final SSymbol selector, } assert target != null; - assert !(args[0] instanceof SFarReference) - : "needs to be guaranted by call to this constructor"; + assert !(args[0] instanceof SFarReference) : "needs to be guaranted by call to this constructor"; assert !(args[0] instanceof SPromise); } @@ -224,8 +227,7 @@ protected static Actor determineTargetAndWrapArguments(final Object[] arguments, // we need to redirect the message to the owner of that far reference Object receiver = WrapReferenceNode.wrapForUse(target, arguments[0], currentSender, null); - assert !(receiver instanceof SPromise) - : "TODO: handle this case as well?? Is it possible? didn't think about it"; + assert !(receiver instanceof SPromise) : "TODO: handle this case as well?? Is it possible? didn't think about it"; if (receiver instanceof SFarReference) { // now we are about to send a message to a far reference, so, it @@ -236,11 +238,10 @@ protected static Actor determineTargetAndWrapArguments(final Object[] arguments, arguments[0] = receiver; - assert !(receiver instanceof SFarReference) - : "this should not happen, because we need to redirect messages to the other actor, and normally we just unwrapped this"; + assert !(receiver instanceof SFarReference) : "this should not happen, because we need to redirect messages to the other actor, and normally we just unwrapped this"; assert !(receiver instanceof SPromise); - for (int i = 1; i < arguments.length; i++) { + for (int i = 1; i < SArguments.getLengthWithoutShadowStack(arguments); i++) { arguments[i] = WrapReferenceNode.wrapForUse(target, arguments[i], originalSender, null); } @@ -267,7 +268,8 @@ public PromiseMessage(final Object[] arguments, final Actor originalSender, } } - public abstract void resolve(Object rcvr, Actor target, Actor sendingActor); + public abstract void resolve(Object rcvr, Actor target, Actor sendingActor, + Object maybeEntry); @Override public final Actor getSender() { @@ -306,20 +308,21 @@ protected AbstractPromiseSendMessage(final SSymbol selector, super(arguments, originalSender, resolver, onReceive, triggerMessageReceiverBreakpoint, triggerPromiseResolverBreakpoint); this.selector = selector; - assert (args[0] instanceof SPromise); - this.originalTarget = (SPromise) args[0]; + assert (args[PROMISE_RCVR_IDX] instanceof SPromise); + this.originalTarget = (SPromise) args[PROMISE_RCVR_IDX]; } @Override - public void resolve(final Object rcvr, final Actor target, final Actor sendingActor) { - determineAndSetTarget(rcvr, target, sendingActor); + public void resolve(final Object rcvr, final Actor target, final Actor sendingActor, + final Object maybeEntry) { + determineAndSetTarget(rcvr, target, sendingActor, maybeEntry); } private void determineAndSetTarget(final Object rcvr, final Actor target, - final Actor sendingActor) { + final Actor sendingActor, final Object maybeEntry) { VM.thisMethodNeedsToBeOptimized("not optimized for compilation"); - args[0] = rcvr; + args[PROMISE_RCVR_IDX] = rcvr; Actor finalTarget = determineTargetAndWrapArguments(args, target, sendingActor, originalSender); @@ -329,6 +332,12 @@ private void determineAndSetTarget(final Object rcvr, final Actor target, this.messageId = Math.min(this.messageId, ActorProcessingThread.currentThread().getSnapshotId()); } + + // save promise resolution entry corresponding to the promise to which the message is + // sent + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + SArguments.saveCausalEntryForPromise(maybeEntry, args[args.length - 1]); + } } @Override @@ -391,19 +400,23 @@ public abstract static class AbstractPromiseCallbackMessage extends PromiseMessa * The promise on which this callback is registered on. */ @CompilationFinal protected SPromise promise; + @CompilationFinal protected SBlock callback; protected AbstractPromiseCallbackMessage(final Actor owner, final SBlock callback, final SResolver resolver, final RootCallTarget onReceive, final boolean triggerMessageReceiverBreakpoint, final boolean triggerPromiseResolverBreakpoint, final SPromise promiseRegisteredOn) { - super(new Object[] {callback, null}, owner, resolver, onReceive, + super(SArguments.getPromiseCallbackArgumentArray(callback), owner, + resolver, onReceive, triggerMessageReceiverBreakpoint, triggerPromiseResolverBreakpoint); this.promise = promiseRegisteredOn; + this.callback = callback; } @Override - public void resolve(final Object rcvr, final Actor target, final Actor sendingActor) { - setPromiseValue(rcvr, sendingActor); + public void resolve(final Object rcvr, final Actor target, final Actor sendingActor, + final Object maybeEntry) { + setPromiseValue(rcvr, sendingActor, maybeEntry); } /** @@ -412,8 +425,34 @@ public void resolve(final Object rcvr, final Actor target, final Actor sendingAc * * @param resolvingActor - the owner of the value, the promise was resolved to. */ - private void setPromiseValue(final Object value, final Actor resolvingActor) { - args[1] = WrapReferenceNode.wrapForUse(originalSender, value, resolvingActor, null); + private void setPromiseValue(final Object value, final Actor resolvingActor, + final Object maybeEntry) { + args[PROMISE_VALUE_IDX] = + WrapReferenceNode.wrapForUse(originalSender, value, resolvingActor, null); + // save promise resolution entry corresponding to the promise to which this callback is + // registered on + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + assert args[args.length - 1] instanceof ShadowStackEntry; +// ShadowStackEntry callPromiseStack = (ShadowStackEntry) args[args.length - 1]; +// boolean promiseGroup = false; +// Node expression = callPromiseStack.getPreviousShadowStackEntry().getExpression(); +// if (expression != null){ +// expression = expression.getParent(); +// if (expression != null) +// if (expression.getParent() instanceof Method) { +// promiseGroup = +// ((Method) expression.getParent()).getName().startsWith("PromiseGroup"); +// }} +//// +// if (promise.isPromiseGroup) { +// SArguments.saveCausalEntryForPromiseGroup(maybeEntry, args[args.length - 1], +// promise.getOwner().getId()); +// } else { + + SArguments.saveCausalEntryForPromise(maybeEntry, args[args.length - 1]); + + } + if (VmSettings.SNAPSHOTS_ENABLED) { this.messageId = Math.min(this.messageId, ActorProcessingThread.currentThread().getSnapshotId()); @@ -422,7 +461,7 @@ private void setPromiseValue(final Object value, final Actor resolvingActor) { @Override public SSymbol getSelector() { - return ((SBlock) args[0]).getMethod().getSignature(); + return ((SBlock) args[PROMISE_RCVR_IDX]).getMethod().getSignature(); } @Override @@ -471,12 +510,22 @@ public final void execute() { assert onReceive.getRootNode() instanceof ReceivedMessage || onReceive.getRootNode() instanceof ReceivedCallback; - onReceive.call(this); + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + assert args[args.length - 1] instanceof ShadowStackEntry; + ShadowStackEntry ssEntry = (ShadowStackEntry) args[args.length - 1]; + args[args.length - 1] = null; + onReceive.call(this, ssEntry); + } else { + onReceive.call(this); + } } public static Actor getActorCurrentMessageIsExecutionOn() { Thread t = Thread.currentThread(); - return ((ActorProcessingThread) t).currentlyExecutingActor; + if (t instanceof ActorProcessingThread) { + return ((ActorProcessingThread) t).currentlyExecutingActor; + } + return null; } public static EventualMessage getCurrentExecutingMessage() { diff --git a/src/som/interpreter/actors/EventualSendNode.java b/src/som/interpreter/actors/EventualSendNode.java index 7da8f7ff3a..465471f6ba 100644 --- a/src/som/interpreter/actors/EventualSendNode.java +++ b/src/som/interpreter/actors/EventualSendNode.java @@ -19,6 +19,7 @@ import com.oracle.truffle.api.source.SourceSection; import som.VM; +import som.interpreter.SArguments; import som.interpreter.SomLanguage; import som.interpreter.actors.EventualMessage.DirectMessage; import som.interpreter.actors.EventualMessage.PromiseSendMessage; @@ -27,7 +28,7 @@ import som.interpreter.actors.RegisterOnPromiseNode.RegisterWhenResolved; import som.interpreter.actors.SPromise.SResolver; import som.interpreter.nodes.ExpressionNode; -import som.interpreter.nodes.InternalObjectArrayNode; +import som.interpreter.nodes.InternalObjectArrayNode.ArgumentEvaluationNode; import som.interpreter.nodes.MessageSendNode; import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode; import som.interpreter.nodes.SOMNode; @@ -38,10 +39,10 @@ import tools.concurrency.KomposTrace; import tools.concurrency.Tags.EventualMessageSend; import tools.concurrency.Tags.ExpressionBreakpoint; +import tools.debugger.breakpoints.Breakpoints; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.SendOp; import tools.debugger.nodes.AbstractBreakpointNode; -import tools.debugger.session.Breakpoints; import tools.dym.DynamicMetrics; import tools.replay.ReplayRecord; import tools.replay.TraceRecord; @@ -52,11 +53,11 @@ public class EventualSendNode extends ExprWithTagsNode { public static final AtomicLong numPromisesAvoided = DynamicMetrics.createLong("Num.Promises.Avoided"); - @Child protected InternalObjectArrayNode arguments; - @Child protected SendNode send; + @Child protected ArgumentEvaluationNode arguments; + @Child protected SendNode send; public EventualSendNode(final SSymbol selector, final int numArgs, - final InternalObjectArrayNode arguments, final SourceSection source, + final ArgumentEvaluationNode arguments, final SourceSection source, final SourceSection sendOperator, final SomLanguage lang) { this.arguments = arguments; this.send = SendNodeGen.create(selector, createArgWrapper(numArgs), @@ -79,6 +80,10 @@ public Object executeGeneric(final VirtualFrame frame) { return send.execute(frame, args); } + public SSymbol getSentSymbol() { + return SOMNode.unwrapIfNecessary(send).getSelector(); + } + public static RootCallTarget createOnReceiveCallTarget(final SSymbol selector, final SourceSection source, final SomLanguage lang) { @@ -204,12 +209,11 @@ protected void sendDirectMessage(final Object[] args, final Actor owner, SFarReference rcvr = (SFarReference) args[0]; Actor target = rcvr.getActor(); - for (int i = 0; i < args.length; i++) { + for (int i = 0; i < SArguments.getLengthWithoutShadowStack(args); i++) { args[i] = wrapArgs[i].execute(args[i], target, owner); } - assert !(args[0] instanceof SFarReference) - : "This should not happen for this specialization, but it is handled in determineTargetAndWrapArguments(.)"; + assert !(args[0] instanceof SFarReference) : "This should not happen for this specialization, but it is handled in determineTargetAndWrapArguments(.)"; assert !(args[0] instanceof SPromise) : "Should not happen either, but just to be sure"; DirectMessage msg = new DirectMessage(target, selector, args, @@ -225,34 +229,46 @@ protected void sendDirectMessage(final Object[] args, final Actor owner, if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.ACTOR_MSG, msg.getMessageId(), - target.getId()); + target.getId(), msg.getSelector(), msg.getTarget().getId(), + msg.getTargetSourceSection()); } target.send(msg, actorPool); } - protected void sendPromiseMessage(final Object[] args, final SPromise rcvr, - final SResolver resolver, final RegisterWhenResolved registerNode) { - assert rcvr.getOwner() == EventualMessage.getActorCurrentMessageIsExecutionOn() - : "think this should be true because the promise is an Object and owned by this specific actor"; + protected void sendPromiseMessage(final VirtualFrame frame, final Object[] args, + final SPromise rcvr, final SResolver resolver, + final RegisterWhenResolved registerNode) { + assert rcvr.getOwner() == EventualMessage.getActorCurrentMessageIsExecutionOn() : "think this should be true because the promise is an Object and owned by this specific actor"; PromiseSendMessage msg = new PromiseSendMessage(selector, args, rcvr.getOwner(), resolver, onReceive, messageReceiverBreakpoint.executeShouldHalt(), promiseResolverBreakpoint.executeShouldHalt()); + Actor target = null; + if (isFarRefRcvr(args)) { + SFarReference farReference = (SFarReference) args[0]; + target = farReference.getActor(); + } + if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.PROMISE_MSG, msg.getMessageId(), - rcvr.getPromiseId()); + rcvr.getPromiseId(), msg.getSelector(), target != null ? target.getId() : -1, + msg.getTargetSourceSection()); } - registerNode.register(rcvr, msg, rcvr.getOwner()); + registerNode.register(frame, rcvr, msg, rcvr.getOwner()); } protected RegisterWhenResolved createRegisterNode() { return new RegisterWhenResolved(actorPool); } + public SSymbol getSelector() { + return selector; + } + @Override public String toString() { return "EventSend[" + selector.toString() + "]"; @@ -272,7 +288,8 @@ public final SPromise toFarRefWithResultPromise(final Object[] args) { } @Specialization(guards = {"isResultUsed()", "isPromiseRcvr(args)"}) - public final SPromise toPromiseWithResultPromise(final Object[] args, + public final SPromise toPromiseWithResultPromise(final VirtualFrame frame, + final Object[] args, @Cached("createRegisterNode()") final RegisterWhenResolved registerNode) { SPromise rcvr = (SPromise) args[0]; @@ -281,7 +298,7 @@ public final SPromise toPromiseWithResultPromise(final Object[] args, false, promiseResolutionBreakpoint.executeShouldHalt(), source); SResolver resolver = SPromise.createResolver(promise); - sendPromiseMessage(args, rcvr, resolver, registerNode); + sendPromiseMessage(frame, args, rcvr, resolver, registerNode); return promise; } @@ -300,7 +317,8 @@ public final SPromise toNearRefWithResultPromise(final Object[] args) { if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.ACTOR_MSG, msg.getMessageId(), - current.getId()); + current.getId(), msg.getSelector(), msg.getTarget().getId(), + msg.getTargetSourceSection()); } if (VmSettings.SENDER_SIDE_REPLAY) { @@ -328,9 +346,10 @@ public final Object toFarRefWithoutResultPromise(final Object[] args) { } @Specialization(guards = {"!isResultUsed()", "isPromiseRcvr(args)"}) - public final Object toPromiseWithoutResultPromise(final Object[] args, + public final Object toPromiseWithoutResultPromise(final VirtualFrame frame, + final Object[] args, @Cached("createRegisterNode()") final RegisterWhenResolved registerNode) { - sendPromiseMessage(args, (SPromise) args[0], null, registerNode); + sendPromiseMessage(frame, args, (SPromise) args[0], null, registerNode); if (VmSettings.DYNAMIC_METRICS) { numPromisesAvoided.getAndIncrement(); @@ -351,7 +370,8 @@ public final Object toNearRefWithoutResultPromise(final Object[] args) { if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.ACTOR_MSG, msg.getMessageId(), - current.getId()); + current.getId(), msg.getSelector(), msg.getTarget().getId(), + msg.getTargetSourceSection()); } if (VmSettings.SENDER_SIDE_REPLAY) { diff --git a/src/som/interpreter/actors/ReceivedMessage.java b/src/som/interpreter/actors/ReceivedMessage.java index f1a76a958e..e62d6a64b4 100644 --- a/src/som/interpreter/actors/ReceivedMessage.java +++ b/src/som/interpreter/actors/ReceivedMessage.java @@ -3,16 +3,21 @@ import java.util.concurrent.CompletableFuture; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.source.SourceSection; +import som.interpreter.Invokable; +import som.interpreter.SArguments; import som.interpreter.SomException; import som.interpreter.SomLanguage; +import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode; +import som.vm.VmSettings; +import som.vmobjects.SInvokable; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntry; public class ReceivedMessage extends ReceivedRootNode { @@ -29,14 +34,36 @@ public ReceivedMessage(final AbstractMessageSendNode onReceive, assert onReceive.getSourceSection() != null; } + @Override + public String getName() { + return selector.toString(); + } + @Override protected Object executeBody(final VirtualFrame frame, final EventualMessage msg, final boolean haltOnResolver, final boolean haltOnResolution) { + ShadowStackEntry resolutionEntry = null; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + ShadowStackEntry entry = SArguments.getShadowStackEntry(frame.getArguments()); + assert entry != null; + + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation onReceiveLocation = + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.ON_RECEIVE_MESSAGE; + // String resolutionValue = (msg.getTarget().getId() + " sent by actor "+ + // msg.getSender().getId()); + resolutionEntry = + ShadowStackEntry.createAtPromiseResolution(entry, (ExpressionNode) onReceive, + onReceiveLocation, ""); + } + try { + assert msg.args[msg.args.length - 1] == null + || !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE; Object result = onReceive.executeEvaluated(frame, msg.args); - resolvePromise(frame, msg.resolver, result, haltOnResolver, haltOnResolution); + resolvePromise(frame, msg.resolver, result, + resolutionEntry, haltOnResolver, haltOnResolution); } catch (SomException exception) { - errorPromise(frame, msg.resolver, exception.getSomObject(), + errorPromise(frame, msg.resolver, exception.getSomObject(), resolutionEntry, haltOnResolver, haltOnResolution); } return null; @@ -81,22 +108,44 @@ private void resolveFuture(final Object result) { public static final class ReceivedCallback extends ReceivedRootNode { @Child protected DirectCallNode onReceive; + private final Invokable onReceiveMethod; - public ReceivedCallback(final RootCallTarget onReceive) { - super(SomLanguage.getLanguage(onReceive.getRootNode()), - onReceive.getRootNode().getSourceSection(), null); - this.onReceive = Truffle.getRuntime().createDirectCallNode(onReceive); + public ReceivedCallback(final SInvokable onReceive) { + super(SomLanguage.getLanguage(onReceive.getInvokable()), + onReceive.getSourceSection(), null); + this.onReceive = Truffle.getRuntime().createDirectCallNode(onReceive.getCallTarget()); + this.onReceiveMethod = onReceive.getInvokable(); + } + + @Override + public String getName() { + return onReceiveMethod.getName(); } @Override protected Object executeBody(final VirtualFrame frame, final EventualMessage msg, final boolean haltOnResolver, final boolean haltOnResolution) { + ShadowStackEntry resolutionEntry = null; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + ShadowStackEntry entry = SArguments.getShadowStackEntry(frame.getArguments()); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || entry != null; + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation onReceiveLocation = + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.ON_WHEN_RESOLVED_BLOCK; + // String resolutionValue = (msg.getTarget().getId() + " send by actor "+ + // msg.getSender().getId()); + resolutionEntry = + ShadowStackEntry.createAtPromiseResolution(entry, onReceiveMethod.getBodyNode(), + onReceiveLocation, ""); + + SArguments.setShadowStackEntry(msg.getArgs(), resolutionEntry); + } + try { Object result = onReceive.call(msg.args); - resolvePromise(frame, msg.resolver, result, haltOnResolver, + resolvePromise(frame, msg.resolver, result, resolutionEntry, haltOnResolver, haltOnResolution); } catch (SomException exception) { - errorPromise(frame, msg.resolver, exception.getSomObject(), + errorPromise(frame, msg.resolver, exception.getSomObject(), resolutionEntry, haltOnResolver, haltOnResolution); } return null; diff --git a/src/som/interpreter/actors/ReceivedRootNode.java b/src/som/interpreter/actors/ReceivedRootNode.java index a4a0f48890..b3f6ca1f34 100644 --- a/src/som/interpreter/actors/ReceivedRootNode.java +++ b/src/som/interpreter/actors/ReceivedRootNode.java @@ -115,7 +115,7 @@ public SourceSection getSourceSection() { } protected final void resolvePromise(final VirtualFrame frame, - final SResolver resolver, final Object result, + final SResolver resolver, final Object result, final Object maybeEntry, final boolean haltOnResolver, final boolean haltOnResolution) { // lazy initialization of resolution node if (resolve == null) { @@ -127,7 +127,7 @@ protected final void resolvePromise(final VirtualFrame frame, resolve = insert(new NullResolver()); } else { resolve = insert( - ResolvePromiseNodeFactory.create(null, null, null, null).initialize(vm)); + ResolveNodeGen.create(null, null, null, null, null).initialize(vm)); } resolve.initialize(sourceSection); this.resolve = resolve; @@ -135,11 +135,12 @@ protected final void resolvePromise(final VirtualFrame frame, } // resolve promise - resolve.executeEvaluated(frame, resolver, result, haltOnResolver, haltOnResolution); + resolve.executeEvaluated(frame, resolver, result, maybeEntry, haltOnResolver, + haltOnResolution); } protected final void errorPromise(final VirtualFrame frame, - final SResolver resolver, final Object exception, + final SResolver resolver, final Object exception, final Object maybeEntry, final boolean haltOnResolver, final boolean haltOnResolution) { // lazy initialization of resolution node if (error == null) { @@ -151,7 +152,7 @@ protected final void errorPromise(final VirtualFrame frame, error = insert(new NullResolver()); } else { error = insert( - ErrorPromiseNodeFactory.create(null, null, null, null).initialize(vm)); + ErrorNodeGen.create(null, null, null, null, null).initialize(vm)); } this.error = error; this.error.initialize(sourceSection); @@ -159,7 +160,8 @@ protected final void errorPromise(final VirtualFrame frame, } // error promise - error.executeEvaluated(frame, resolver, exception, haltOnResolver, haltOnResolution); + error.executeEvaluated(frame, resolver, exception, maybeEntry, haltOnResolver, + haltOnResolution); } public MessageSerializationNode getSerializer() { @@ -175,7 +177,7 @@ public static final class NullResolver extends AbstractPromiseResolutionNode { @Override public Object executeEvaluated(final VirtualFrame frame, - final SResolver receiver, final Object argument, + final SResolver receiver, final Object argument, final Object maybeEntry, final boolean haltOnResolver, final boolean haltOnResolution) { assert receiver == null; if (VmSettings.DYNAMIC_METRICS) { @@ -186,7 +188,8 @@ public Object executeEvaluated(final VirtualFrame frame, @Override public Object executeEvaluated(final VirtualFrame frame, final Object rcvr, - final Object firstArg, final Object secondArg, final Object thirdArg) { + final Object firstArg, final Object secondArg, final Object thirdArg, + final Object fourthArg) { if (VmSettings.DYNAMIC_METRICS) { numImplicitNullResolutions.getAndIncrement(); } diff --git a/src/som/interpreter/actors/RegisterOnPromiseNode.java b/src/som/interpreter/actors/RegisterOnPromiseNode.java index 354fa332af..d5d7c4d131 100644 --- a/src/som/interpreter/actors/RegisterOnPromiseNode.java +++ b/src/som/interpreter/actors/RegisterOnPromiseNode.java @@ -4,13 +4,17 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicLong; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import som.interpreter.SArguments; import som.interpreter.actors.EventualMessage.AbstractPromiseSendMessage; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.interpreter.actors.SPromise.SReplayPromise; import som.interpreter.actors.SPromise.STracingPromise; import som.vm.VmSettings; +import som.vmobjects.SBlock; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.dym.DynamicMetrics; import tools.replay.ReplayRecord; import tools.replay.TraceRecord; @@ -34,7 +38,8 @@ public RegisterWhenResolved(final ForkJoinPool actorPool) { } } - public void register(final SPromise promise, final PromiseMessage msg, + public void register(final VirtualFrame frame, final SPromise promise, + final PromiseMessage msg, final Actor current) { Object promiseValue; @@ -75,6 +80,50 @@ public void register(final SPromise promise, final PromiseMessage msg, } if (!promise.isResolvedUnsync()) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // get info about the resolution context from the promise, + // we want to know where it was resolved, where the value is coming from + for (Object obj : msg.args) { + boolean promiseComplete = + (obj instanceof SPromise) && promise.isCompleted(); + // boolean promiseChained = (obj instanceof SPromise) && !((SPromise) + // promise).isCompleted(); + if (obj instanceof SBlock || promiseComplete) { + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation onReceiveLocation = + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.ON_WHEN_RESOLVED; + // onReceiveLocation.setArg(msg.getTarget().getId() + " send by actor "+ + // msg.getSender().getId()); + // for whenResolved blocks or if promise is resolved, then create + // EntryForPromiseResolution + ShadowStackEntry resolutionEntry = ShadowStackEntry.createAtPromiseResolution( + SArguments.getShadowStackEntry(frame), + getParent().getParent(), onReceiveLocation, "", promise.promiseGroupOrNull()); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + || resolutionEntry != null; + SArguments.updateShadowStackEntry(msg.args, resolutionEntry); + } + // else if (promiseChained) { + // ShadowStackEntry entry = (ShadowStackEntry) msg.args[msg.args.length - 1]; + // assert entry != null && entry instanceof ShadowStackEntry.EntryAtMessageSend; + // ShadowStackEntry shadowStackEntry = SArguments.getShadowStackEntry(frame); + // + // // entry.setPreviousShadowStackEntry(shadowStackEntry); + // + // so.println("-register msg args: "+entry.getSourceSection()); + // so.println("shadow: "+shadowStackEntry.getSourceSection()); + + // assert maybeEntry != null && maybeEntry instanceof + // ShadowStackEntry.EntryForPromiseResolution; + // assert args[args.length - 1] instanceof ShadowStackEntry.EntryAtMessageSend; + // ShadowStackEntry.EntryAtMessageSend current = + // (ShadowStackEntry.EntryAtMessageSend) args[args.length - 1]; + // SArguments.addEntryForPromiseResolution(current, + // (ShadowStackEntry.EntryForPromiseResolution) maybeEntry); + + // } + } + } + if (VmSettings.SENDER_SIDE_REPLAY) { ReplayRecord npr = current.getNextReplayEvent(); assert npr.type == TraceRecord.MESSAGE; @@ -118,7 +167,7 @@ public void register(final SPromise promise, final PromiseMessage msg, if (VmSettings.DYNAMIC_METRICS) { numScheduledWhenResolved.incrementAndGet(); } - schedule.execute(promise, msg, current); + schedule.execute(frame, promise, msg, current); } } } @@ -134,7 +183,8 @@ public RegisterOnError(final ForkJoinPool actorPool) { } } - public void register(final SPromise promise, final PromiseMessage msg, + public void register(final VirtualFrame frame, final SPromise promise, + final PromiseMessage msg, final Actor current) { Object promiseValue; @@ -158,6 +208,18 @@ public void register(final SPromise promise, final PromiseMessage msg, if (!promise.isErroredUnsync()) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // TODO: I think, we need the info about the resolution context from the promise + // we want to know where it was resolved, where the value is coming from + ShadowStackEntry resolutionEntry = ShadowStackEntry.createAtPromiseResolution( + SArguments.getShadowStackEntry(frame), + getParent().getParent(), + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.ON_WHEN_RESOLVED_ERROR, + "", promise.promiseGroupOrNull()); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || resolutionEntry != null; + SArguments.setShadowStackEntry(msg.args, resolutionEntry); + } + if (VmSettings.SENDER_SIDE_TRACING) { // This is whenResolved promiseMsgSend.record(((STracingPromise) promise).version); @@ -195,7 +257,7 @@ public void register(final SPromise promise, final PromiseMessage msg, if (VmSettings.DYNAMIC_METRICS) { numScheduledOnError.incrementAndGet(); } - schedule.execute(promise, msg, current); + schedule.execute(frame, promise, msg, current); } } } diff --git a/src/som/interpreter/actors/ResolveNode.java b/src/som/interpreter/actors/ResolveNode.java new file mode 100644 index 0000000000..6e178b6a60 --- /dev/null +++ b/src/som/interpreter/actors/ResolveNode.java @@ -0,0 +1,46 @@ +package som.interpreter.actors; + +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; + +import som.interpreter.SArguments; +import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntry; + + +public abstract class ResolveNode extends AbstractPromiseResolutionNode { + + /** + * Normal case, when the promise is resolved with a value that's not a promise. + * Here we need to distinguish the explicit promises to ask directly to the promise + * if a promise resolution breakpoint was set. + */ + @Specialization(guards = {"notAPromise(result)"}) + public SPromise.SResolver normalResolution(final VirtualFrame frame, + final SPromise.SResolver resolver, final Object result, final Object maybeEntry, + final boolean haltOnResolver, final boolean haltOnResolution) { + SPromise promise = resolver.getPromise(); + + // this is needed to suspend on explicit promises (which resolved to a a value different + // from another promise) + if (haltOnResolver || promise.getHaltOnResolver()) { + haltNode.executeEvaluated(frame, result); + } + + //ShadowStackEntry resolutionEntry = null; +// if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { +// ShadowStackEntry entry = SArguments.getShadowStackEntry(frame); +// assert entry != null; +// final ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation location = +// ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.SUCCESSFUL; +// resolutionEntry = +// ShadowStackEntry.createAtPromiseResolution(entry, this.getParent(), location, +// ""); // "value: " + result.toString() +// SArguments.saveCausalEntryForPromise(maybeEntry, resolutionEntry); +// } + + resolvePromise(SPromise.Resolution.SUCCESSFUL, resolver, result, maybeEntry, + haltOnResolution || promise.getHaltOnResolution(), frame, this.getParent()); + return resolver; + } +} diff --git a/src/som/interpreter/actors/ResolvePromiseNode.java b/src/som/interpreter/actors/ResolvePromiseNode.java index 3b6b1c34c8..f3019f0485 100644 --- a/src/som/interpreter/actors/ResolvePromiseNode.java +++ b/src/som/interpreter/actors/ResolvePromiseNode.java @@ -3,37 +3,55 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.GenerateWrapper; +import com.oracle.truffle.api.instrumentation.ProbeNode; import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.api.source.SourceSection; import bd.primitives.Primitive; +import bd.primitives.nodes.WithContext; import bd.tools.nodes.Operation; -import som.interpreter.actors.SPromise.Resolution; +import som.VM; +import som.interpreter.SArguments; import som.interpreter.actors.SPromise.SResolver; +import som.interpreter.nodes.nary.BinaryExpressionNode; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.dym.Tags.ComplexPrimitiveOperation; +@GenerateWrapper @GenerateNodeFactory -@Primitive(primitive = "actorsResolve:with:isBPResolver:isBPResolution:") -public abstract class ResolvePromiseNode extends AbstractPromiseResolutionNode - implements Operation { - /** - * Normal case, when the promise is resolved with a value that's not a promise. - * Here we need to distinguish the explicit promises to ask directly to the promise - * if a promise resolution breakpoint was set. - */ - @Specialization(guards = {"notAPromise(result)"}) - public SResolver normalResolution(final VirtualFrame frame, - final SResolver resolver, final Object result, - final boolean haltOnResolver, final boolean haltOnResolution) { - SPromise promise = resolver.getPromise(); - - if (haltOnResolver || promise.getHaltOnResolver()) { - haltNode.executeEvaluated(frame, result); - } +@Primitive(primitive = "actorsResolve:with:") +public abstract class ResolvePromiseNode extends BinaryExpressionNode + implements Operation, WithContext { + @Child protected ResolveNode resolve; + + public ResolvePromiseNode() { + resolve = ResolveNodeGen.create(null, null, null, null, null); + } - resolvePromise(Resolution.SUCCESSFUL, resolver, result, - haltOnResolution || promise.getHaltOnResolution()); - return resolver; + @Override + public ResolvePromiseNode initialize(final VM vm) { + resolve.initialize(vm); + return this; + } + + @Override + @SuppressWarnings("unchecked") + public ResolvePromiseNode initialize(final SourceSection sourceSection) { + super.initialize(sourceSection); + resolve.initialize(sourceSection); + return this; + } + + public abstract SResolver executeEvaluated(VirtualFrame frame, SResolver resolver, + Object resultObj); + + @Specialization + public SResolver normalResolution(final VirtualFrame frame, final SResolver resolver, + final Object result) { + ShadowStackEntry entry = SArguments.getShadowStackEntry(frame); + return (SResolver) resolve.executeEvaluated(frame, resolver, result, entry, false, false); } @Override @@ -54,6 +72,11 @@ public String getOperation() { } } + @Override + public WrapperNode createWrapper(final ProbeNode probe) { + return new ResolvePromiseNodeWrapper(this, probe); + } + @Override public int getNumArguments() { return 5; diff --git a/src/som/interpreter/actors/SPromise.java b/src/som/interpreter/actors/SPromise.java index 045552c391..7fe245a61b 100644 --- a/src/som/interpreter/actors/SPromise.java +++ b/src/som/interpreter/actors/SPromise.java @@ -1,29 +1,35 @@ package som.interpreter.actors; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; +import java.util.*; import java.util.Map.Entry; -import java.util.PriorityQueue; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicLong; + +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.source.SourceSection; + + +import som.interpreter.SArguments; import som.interpreter.SomLanguage; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.interpreter.objectstorage.ClassFactory; import som.vm.Activity; import som.vm.VmSettings; +import som.vm.constants.Classes; +import som.vmobjects.SArray; import som.vmobjects.SClass; import som.vmobjects.SObjectWithClass; import tools.concurrency.KomposTrace; import tools.concurrency.TracingActivityThread; import tools.concurrency.TracingActors.TracingActor; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.debugger.entities.PassiveEntityType; import tools.dym.DynamicMetrics; import tools.replay.PassiveEntityWithEvents; @@ -51,6 +57,16 @@ public enum Resolution { UNRESOLVED, ERRONEOUS, SUCCESSFUL, CHAINED } + public boolean isPromiseGroup; + private Map> promiseGroupData = new HashMap(); + private int promiseGroupCompletedPromisesCount = 0; + public List groupRoots = new LinkedList(); + + public void setPromiseGroupRoot(SPromise p) { + groupRoots.add(p); + } + + @CompilationFinal private static SClass promiseClass; public static SPromise createPromise(final Actor owner, @@ -71,6 +87,45 @@ public static SPromise createPromise(final Actor owner, } } + public void addPromiseToGroup(SPromise promise){ + assert !promiseGroupData.containsKey(promise); + isPromiseGroup = true; + Object resolutionValue = null; + if( promise.isResolvedUnsync()) { + resolutionValue = promise.value; + promiseGroupCompletedPromisesCount++; + } + promiseGroupData.put(promise,new AbstractMap.SimpleEntry<>(resolutionValue,null)); + } + + public SPromise promiseGroupOrNull() { + return isPromiseGroup ? this : null; + } + + public List getAsyncStacks(){ + assert isPromiseGroup; + return promiseGroupData.values().stream().map(x -> x.getValue()).toList(); + } + + public synchronized void onResolvedGroupPromise(SPromise promise, Object resolvedValue, ForkJoinPool actorPool, VirtualFrame frame, Object maybeEntry){ + promiseGroupCompletedPromisesCount++; + promiseGroupData.put(promise, new AbstractMap.SimpleEntry(resolvedValue, maybeEntry)); + if (promiseGroupCompletedPromisesCount == promiseGroupData.size()) { + Object promiseValues = new SArray.SImmutableArray(promiseGroupData.values().stream().map(x -> x.getKey()).toArray(), Classes.valueArrayClass); + Object wrapped = WrapReferenceNode.wrapForUse(this.owner, promiseValues, this.owner, null); + SResolver.resolveAndTriggerListenersUnsynced(Resolution.SUCCESSFUL, promiseValues, wrapped, this, this.owner, actorPool, maybeEntry, false, ValueProfile.createClassProfile(), frame, null, null, null); + } + + } + + public void triggerCallInRootPromiseGroup(Object resolutionValue,ForkJoinPool actorPool, VirtualFrame frame, Object maybeEntry){ + if (!groupRoots.isEmpty()) { + for (SPromise root : groupRoots){ + root.onResolvedGroupPromise(this,resolutionValue,actorPool, frame, maybeEntry); + } + } + } + /** Used by snapshot deserialization. */ public static SPromise createResolved(final Actor owner, final Object value, final Resolution state) { @@ -107,15 +162,15 @@ public static SPromise createResolved(final Actor owner, final Object value, /** * Trigger breakpoint at the point where the promise is resolved with a value. */ - private final boolean haltOnResolver; + private boolean haltOnResolver; /** * Trigger a breakpoint when executing a resolution callback. */ private boolean haltOnResolution; - protected SPromise(final Actor owner, final boolean haltOnResolver, - final boolean haltOnResolution) { + public SPromise(final Actor owner, final boolean haltOnResolver, + final boolean haltOnResolution) { super(promiseClass, promiseClass.getInstanceFactory()); assert owner != null; this.owner = owner; @@ -126,9 +181,23 @@ protected SPromise(final Actor owner, final boolean haltOnResolver, assert promiseClass != null; } + public SPromise(final Actor owner, final boolean haltOnResolver, + final boolean haltOnResolution,Resolution resolutionState, Object value) { + super(promiseClass, promiseClass.getInstanceFactory()); + assert owner != null; + this.owner = owner; + this.haltOnResolver = haltOnResolver; + this.haltOnResolution = haltOnResolution; + + this.resolutionState = resolutionState; + this.value = value; + + assert promiseClass != null; + } + @Override public String toString() { - String r = "Promise[" + owner.toString(); + String r = "Promise[" + getPromiseId() + ", " + owner.toString(); r += ", " + resolutionState.name(); return r + (value == null ? "" : ", " + value.toString()) + "]"; } @@ -138,6 +207,11 @@ public final boolean isValue() { return false; } + public final Object getValueForPromiseGroupResolution() { + assert resolutionState == Resolution.SUCCESSFUL || resolutionState == Resolution.ERRONEOUS; + return value; + } + /** * Do not use for things other than serializing Promises. * @@ -261,13 +335,13 @@ private void registerMoreOnError(final PromiseMessage msg) { protected final void scheduleCallbacksOnResolution(final Object result, final PromiseMessage msg, final Actor current, - final ForkJoinPool actorPool, final boolean haltOnResolution) { + final ForkJoinPool actorPool, final Object maybeEntry, final boolean haltOnResolution) { // when a promise is resolved, we need to schedule all the // #whenResolved:/#onError:/... callbacks msgs as well as all eventual send // msgs to the promise assert owner != null; - msg.resolve(result, owner, current); + msg.resolve(result, owner, current, maybeEntry); // if the promise had a haltOnResolution, // the message needs to break on receive @@ -313,8 +387,7 @@ public final Resolution getResolutionStateUnsync() { } public final boolean assertNotCompleted() { - assert !isCompleted() - : "Not sure yet what to do with re-resolving of promises? just ignore it? Error?"; + assert !isCompleted() : "Not sure yet what to do with re-resolving of promises? just ignore it? Error?"; assert value == null : "If it isn't resolved yet, it shouldn't have a value"; return true; } @@ -416,7 +489,8 @@ protected SReplayPromise(final Actor owner, final boolean haltOnResolver, @TruffleBoundary public void handleReplayResolution(final boolean haltOnResolution, final Actor resolver, - final Resolution type, final ValueProfile whenResolvedProfile) { + final Resolution type, final ValueProfile whenResolvedProfile, + final Object maybeEntry) { assert !handled; handled = true; @@ -466,7 +540,7 @@ public void handleReplayResolution(final boolean haltOnResolution, final Actor r if (todo != null) { while (!todo.isEmpty()) { PromiseMessage pm = todo.poll(); - pm.resolve(this.value, owner, (Actor) current); + pm.resolve(this.value, owner, (Actor) current, maybeEntry); npr = current.getNextReplayEvent(); assert npr.type == TraceRecord.MESSAGE : "was " + npr.type + " in " @@ -565,13 +639,15 @@ private void tryPerformDelayedResolution() { if (toSend != null) { while (!toSend.isEmpty()) { PromiseMessage pm = toSend.remove(); - pm.resolve(this.value, owner, resolver); + // TODO: can we better than passing null as maybeEntry? + Object maybeEntry = null; + pm.resolve(this.value, owner, resolver, maybeEntry); npr = current.getNextReplayEvent(); assert npr.type == TraceRecord.MESSAGE; pm.setReplayVersion(npr.eventNo); this.scheduleCallbacksOnResolution(this.value, pm, resolver, - SomLanguage.getCurrent().getVM().getActorPool(), + SomLanguage.getCurrent().getVM().getActorPool(), maybeEntry, haltOnResolution); } } @@ -605,11 +681,13 @@ private void sendDelayedMessages() { if (delayedMessages != null) { Activity current = TracingActivityThread.currentThread().getActivity(); for (Entry> e : delayedMessages.entrySet()) { + // TODO: can we do better than maybeEntry == null? + Object maybeEntry = null; PromiseMessage pm = e.getKey(); LinkedList events = e.getValue(); current.getReplayEventBuffer().addAll(0, events); this.scheduleCallbacksOnResolution(value, pm, (Actor) current, - SomLanguage.getCurrent().getVM().getActorPool(), haltOnResolution); + SomLanguage.getCurrent().getVM().getActorPool(), maybeEntry, haltOnResolution); } } } @@ -677,11 +755,13 @@ private void resolveChainedPromisesReplay(final Resolution type, if (replayChainedPromises != null) { while (!replayChainedPromises.isEmpty()) { SReplayPromise rp = replayChainedPromises.remove(); + // TODO: can we do better than maybeEntry == null? + Object maybeEntry = null; Object wrapped = WrapReferenceNode.wrapForUse(rp.owner, value, resolver, null); SResolver.resolveAndTriggerListenersUnsynced(type, value, wrapped, rp, resolver, - SomLanguage.getCurrent().getVM().getActorPool(), haltOnResolution, - whenResolvedProfile, null, null); + SomLanguage.getCurrent().getVM().getActorPool(), maybeEntry, haltOnResolution, + whenResolvedProfile, null, null, null, null); } } } @@ -770,48 +850,79 @@ public boolean assertNotCompleted() { */ // TODO: solve the TODO and then remove the TruffleBoundary, this might even need to go // into a node - @TruffleBoundary protected static void resolveChainedPromisesUnsync(final Resolution type, final SPromise promise, final Object result, final Actor current, - final ForkJoinPool actorPool, final boolean haltOnResolution, - final ValueProfile whenResolvedProfile, final RecordOneEvent record, + final ForkJoinPool actorPool, final Object maybeEntry, final boolean haltOnResolution, + final ValueProfile whenResolvedProfile, final VirtualFrame frame, + final Node expression, final RecordOneEvent record, final RecordOneEvent recordStop) { // TODO: we should change the implementation of chained promises to // always move all the handlers to the other promise, then we // don't need to worry about traversing the chain, which can // lead to a stack overflow. // TODO: restore 10000 as parameter in testAsyncDeeplyChainedResolution - if (promise.chainedPromise != null) { - SPromise chainedPromise = promise.chainedPromise; - promise.chainedPromise = null; - Object wrapped = - WrapReferenceNode.wrapForUse(chainedPromise.owner, result, current, null); - resolveAndTriggerListenersUnsynced(type, result, wrapped, - chainedPromise, current, actorPool, - chainedPromise.haltOnResolution, whenResolvedProfile, record, recordStop); - resolveMoreChainedPromisesUnsynced(type, promise, result, current, - actorPool, haltOnResolution, whenResolvedProfile, record, recordStop); + if (promise.chainedPromise == null) { + return; + } + + // We do not want to invalidate here, + // but we can't have a boundary because we need the frame + CompilerDirectives.transferToInterpreter(); + + SPromise chainedPromise = promise.chainedPromise; + promise.chainedPromise = null; + Object wrapped = + WrapReferenceNode.wrapForUse(chainedPromise.owner, result, current, null); + + ShadowStackEntry resolutionEntry = null; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // MaterializedFrame context = promise.getContext(); + + ShadowStackEntry entry = SArguments.getShadowStackEntry(frame.getArguments()); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || maybeEntry != null; + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation location = + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.CHAINED; + resolutionEntry = + ShadowStackEntry.createAtPromiseResolution(entry, expression, location, + "promise: " + promise.toString()); + + SArguments.saveCausalEntryForPromise(maybeEntry, resolutionEntry); } + + resolveAndTriggerListenersUnsynced(type, result, wrapped, + chainedPromise, current, actorPool, resolutionEntry, + chainedPromise.haltOnResolution, whenResolvedProfile, frame, expression, record, + recordStop); + resolveMoreChainedPromisesUnsynced(type, promise, result, current, + actorPool, resolutionEntry, haltOnResolution, whenResolvedProfile, + frame, expression, record, recordStop); } /** * Resolve the other promises that has been chained to the first promise. */ - @TruffleBoundary private static void resolveMoreChainedPromisesUnsynced(final Resolution type, final SPromise promise, final Object result, final Actor current, - final ForkJoinPool actorPool, final boolean haltOnResolution, - final ValueProfile whenResolvedProfile, final RecordOneEvent record, + final ForkJoinPool actorPool, final Object maybeEntry, final boolean haltOnResolution, + final ValueProfile whenResolvedProfile, final VirtualFrame frame, + final Node expression, final RecordOneEvent record, final RecordOneEvent recordStop) { - if (promise.chainedPromiseExt != null) { - ArrayList chainedPromiseExt = promise.chainedPromiseExt; - promise.chainedPromiseExt = null; - - for (SPromise p : chainedPromiseExt) { - Object wrapped = WrapReferenceNode.wrapForUse(p.owner, result, current, null); - resolveAndTriggerListenersUnsynced(type, result, wrapped, p, current, - actorPool, haltOnResolution, whenResolvedProfile, record, recordStop); - } + + if (promise.chainedPromiseExt == null) { + return; + } + + // We do not want to invalidate here, + // but we can't have a boundary because we need the frame + CompilerDirectives.transferToInterpreter(); + ArrayList chainedPromiseExt = promise.chainedPromiseExt; + promise.chainedPromiseExt = null; + + for (SPromise p : chainedPromiseExt) { + Object wrapped = WrapReferenceNode.wrapForUse(p.owner, result, current, null); + resolveAndTriggerListenersUnsynced(type, result, wrapped, p, current, + actorPool, maybeEntry, haltOnResolution, whenResolvedProfile, frame, expression, + record, recordStop); } } @@ -822,8 +933,9 @@ private static void resolveMoreChainedPromisesUnsynced(final Resolution type, */ protected static void resolveAndTriggerListenersUnsynced(final Resolution type, final Object result, final Object wrapped, final SPromise p, final Actor current, - final ForkJoinPool actorPool, final boolean haltOnResolution, - final ValueProfile whenResolvedProfile, final RecordOneEvent tracePromiseResolution2, + final ForkJoinPool actorPool, final Object maybeEntry, final boolean haltOnResolution, + final ValueProfile whenResolvedProfile, final VirtualFrame frame, + final Node expression, final RecordOneEvent tracePromiseResolution2, final RecordOneEvent tracePromiseResolutionEnd2) { assert !(result instanceof SPromise); @@ -858,7 +970,7 @@ protected static void resolveAndTriggerListenersUnsynced(final Resolution type, if (VmSettings.SENDER_SIDE_REPLAY) { ((SReplayPromise) p).handleReplayResolution(haltOnResolution, current, type, - whenResolvedProfile); + whenResolvedProfile, maybeEntry); } } @@ -867,18 +979,28 @@ protected static void resolveAndTriggerListenersUnsynced(final Resolution type, } if (type == Resolution.SUCCESSFUL) { - scheduleAllWhenResolvedUnsync(p, result, current, actorPool, haltOnResolution, + scheduleAllWhenResolvedUnsync(p, result, current, actorPool, maybeEntry, + haltOnResolution, whenResolvedProfile); } else { assert type == Resolution.ERRONEOUS; - scheduleAllOnErrorUnsync(p, result, current, actorPool, haltOnResolution); + scheduleAllOnErrorUnsync(p, result, current, actorPool, maybeEntry, + haltOnResolution); } - resolveChainedPromisesUnsync(type, p, result, current, actorPool, haltOnResolution, - whenResolvedProfile, tracePromiseResolution2, tracePromiseResolutionEnd2); + resolveChainedPromisesUnsync(type, p, result, current, actorPool, maybeEntry, + haltOnResolution, + whenResolvedProfile, frame, expression, tracePromiseResolution2, + tracePromiseResolutionEnd2); if (VmSettings.SENDER_SIDE_TRACING) { tracePromiseResolutionEnd2.record(((STracingPromise) p).version); } + Object toPassEntry = maybeEntry; + if (maybeEntry instanceof ShadowStackEntry){ + toPassEntry = ShadowStackEntry.createAtPromiseResolution((ShadowStackEntry) maybeEntry,expression, ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.SUCCESSFUL,wrapped.toString()); + } + + p.triggerCallInRootPromiseGroup(result,actorPool,frame,toPassEntry); } } @@ -887,6 +1009,7 @@ protected static void resolveAndTriggerListenersUnsynced(final Resolution type, */ protected static void scheduleAllWhenResolvedUnsync(final SPromise promise, final Object result, final Actor current, final ForkJoinPool actorPool, + final Object maybeEntry, final boolean haltOnResolution, final ValueProfile whenResolvedProfile) { if (promise.whenResolved != null) { PromiseMessage whenResolved = promise.whenResolved; @@ -900,8 +1023,9 @@ protected static void scheduleAllWhenResolvedUnsync(final SPromise promise, } promise.scheduleCallbacksOnResolution(result, - whenResolvedProfile.profile(whenResolved), current, actorPool, haltOnResolution); - scheduleExtensions(promise, whenResolvedExt, result, current, actorPool, + whenResolvedProfile.profile(whenResolved), current, actorPool, maybeEntry, + haltOnResolution); + scheduleExtensions(promise, whenResolvedExt, result, current, actorPool, maybeEntry, haltOnResolution); } } @@ -913,12 +1037,13 @@ protected static void scheduleAllWhenResolvedUnsync(final SPromise promise, private static void scheduleExtensions(final SPromise promise, final ArrayList extension, final Object result, final Actor current, - final ForkJoinPool actorPool, final boolean haltOnResolution) { + final ForkJoinPool actorPool, final Object maybeEntry, + final boolean haltOnResolution) { if (extension != null) { for (int i = 0; i < extension.size(); i++) { PromiseMessage callbackOrMsg = extension.get(i); promise.scheduleCallbacksOnResolution(result, callbackOrMsg, current, - actorPool, haltOnResolution); + actorPool, maybeEntry, haltOnResolution); } } } @@ -928,7 +1053,8 @@ private static void scheduleExtensions(final SPromise promise, */ protected static void scheduleAllOnErrorUnsync(final SPromise promise, final Object result, final Actor current, - final ForkJoinPool actorPool, final boolean haltOnResolution) { + final ForkJoinPool actorPool, final Object maybeEntry, + final boolean haltOnResolution) { if (promise.onError != null) { PromiseMessage onError = promise.onError; ArrayList onErrorExt = promise.onErrorExt; @@ -940,9 +1066,10 @@ protected static void scheduleAllOnErrorUnsync(final SPromise promise, numScheduledOnError.addAndGet(1 + count); } - promise.scheduleCallbacksOnResolution(result, onError, current, actorPool, + promise.scheduleCallbacksOnResolution(result, onError, current, actorPool, maybeEntry, + haltOnResolution); + scheduleExtensions(promise, onErrorExt, result, current, actorPool, maybeEntry, haltOnResolution); - scheduleExtensions(promise, onErrorExt, result, current, actorPool, haltOnResolution); } } } @@ -965,4 +1092,8 @@ boolean getHaltOnResolution() { public void enableHaltOnResolution() { haltOnResolution = true; } + + public void enableHaltOnResolver() { + haltOnResolver = true; + } } diff --git a/src/som/interpreter/actors/SchedulePromiseHandlerNode.java b/src/som/interpreter/actors/SchedulePromiseHandlerNode.java index 2214775407..5ef8c76f47 100644 --- a/src/som/interpreter/actors/SchedulePromiseHandlerNode.java +++ b/src/som/interpreter/actors/SchedulePromiseHandlerNode.java @@ -4,15 +4,18 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.IntValueProfile; import som.VM; +import som.interpreter.SArguments; import som.interpreter.actors.EventualMessage.PromiseCallbackMessage; import som.interpreter.actors.EventualMessage.PromiseMessage; import som.interpreter.actors.EventualMessage.PromiseSendMessage; import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.replay.ReplayRecord; import tools.replay.TraceRecord; @@ -33,10 +36,11 @@ protected SchedulePromiseHandlerNode(final ForkJoinPool actorPool) { this.actorPool = actorPool; } - public abstract void execute(SPromise promise, PromiseMessage msg, Actor current); + public abstract void execute(VirtualFrame frame, SPromise promise, PromiseMessage msg, + Actor current); @Specialization - public final void schedule(final SPromise promise, + public final void schedule(final VirtualFrame frame, final SPromise promise, final PromiseCallbackMessage msg, final Actor current, @Cached("createWrapper()") final WrapReferenceNode wrapper) { assert promise.getOwner() != null; @@ -44,6 +48,20 @@ public final void schedule(final SPromise promise, msg.args[PromiseMessage.PROMISE_VALUE_IDX] = wrapper.execute( promise.getValueUnsync(), msg.originalSender, current); + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // Get info about the resolution context from the promise + // we want to know where it was resolved, where the value is coming from + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation onReceiveLocation = + ShadowStackEntry.EntryForPromiseResolution.ResolutionLocation.ON_SCHEDULE_PROMISE; + // onReceiveLocation.setArg(msg.getTarget().getId() + " send by actor "+ + // msg.getSender().getId()); + ShadowStackEntry resolutionEntry = ShadowStackEntry.createAtPromiseResolution( + SArguments.getShadowStackEntry(frame), + getParent().getParent(), onReceiveLocation, ""); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || resolutionEntry != null; + SArguments.setShadowStackEntry(msg.args, resolutionEntry); + } + if (VmSettings.SENDER_SIDE_REPLAY) { ReplayRecord npr = current.getNextReplayEvent(); assert npr.type == TraceRecord.MESSAGE; @@ -65,8 +83,7 @@ public final void schedule(final SPromise promise, Object receiver = rcvrWrapper.execute(promise.getValueUnsync(), finalTarget, current); - assert !(receiver instanceof SPromise) - : "TODO: handle this case as well?? Is it possible? didn't think about it"; + assert !(receiver instanceof SPromise) : "TODO: handle this case as well?? Is it possible? didn't think about it"; // TODO: might want to handle that in a specialization if (receiver instanceof SFarReference) { @@ -76,10 +93,12 @@ public final void schedule(final SPromise promise, receiver = ((SFarReference) receiver).getValue(); } + // TODO: we already have a shadow stack entry here, Don't think we need to do anything + // about it + msg.args[PromiseMessage.PROMISE_RCVR_IDX] = receiver; - assert !(receiver instanceof SFarReference) - : "this should not happen, because we need to redirect messages to the other actor, and normally we just unwrapped this"; + assert !(receiver instanceof SFarReference) : "this should not happen, because we need to redirect messages to the other actor, and normally we just unwrapped this"; assert !(receiver instanceof SPromise); wrapArguments(msg, finalTarget, argWrapper); @@ -102,7 +121,8 @@ public final void schedule(final SPromise promise, private void wrapArguments(final PromiseSendMessage msg, final Actor finalTarget, final WrapReferenceNode argWrapper) { // TODO: break that out into nodes - for (int i = 1; i < numArgs.profile(msg.args.length); i++) { + for (int i = + 1; i < numArgs.profile(SArguments.getLengthWithoutShadowStack(msg.args)); i++) { msg.args[i] = argWrapper.execute(msg.args[i], finalTarget, msg.originalSender); } } diff --git a/src/som/interpreter/actors/TransferObject.java b/src/som/interpreter/actors/TransferObject.java index 92d247eb0d..6d52cf9aaf 100644 --- a/src/som/interpreter/actors/TransferObject.java +++ b/src/som/interpreter/actors/TransferObject.java @@ -50,8 +50,7 @@ public static SObject transfer(final SObject obj, final Actor origin, final Actor target, final Map transferedObjects) { assert obj.getSOMClass() - .isTransferObject() - : "only TransferObjects should be handled here"; + .isTransferObject() : "only TransferObjects should be handled here"; assert !obj.isValue() : "TransferObjects can't be Values"; ObjectLayout layout = obj.getObjectLayout(); diff --git a/src/som/interpreter/nodes/ExceptionSignalingNode.java b/src/som/interpreter/nodes/ExceptionSignalingNode.java index 927d5321e7..6efdadcdbb 100644 --- a/src/som/interpreter/nodes/ExceptionSignalingNode.java +++ b/src/som/interpreter/nodes/ExceptionSignalingNode.java @@ -3,10 +3,12 @@ import java.util.function.Supplier; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; import bd.primitives.nodes.PreevaluatedExpression; +import som.interpreter.SArguments; import som.vm.Symbols; import som.vm.constants.KernelObj; import som.vmobjects.SClass; @@ -44,7 +46,7 @@ public static ExceptionSignalingNode createNode(final Supplier resolver return new ResolveModule(exceptionSelector, factorySelector, sourceSection, resolver); } - public abstract Object signal(Object... args); + public abstract Object signal(VirtualFrame frame, Object... args); private static final class ResolvedModule extends ExceptionSignalingNode { @Child protected ExpressionNode getExceptionClassNode; @@ -63,22 +65,14 @@ private ResolvedModule(final SObject module, final SSymbol exceptionSelector, } @Override - public Object signal(final Object... args) { + public Object signal(VirtualFrame frame, final Object... args) { SClass exceptionClass = - (SClass) ((PreevaluatedExpression) getExceptionClassNode).doPreEvaluated(null, + (SClass) ((PreevaluatedExpression) getExceptionClassNode).doPreEvaluated(frame, new Object[] {module}); - return ((PreevaluatedExpression) signalExceptionNode).doPreEvaluated(null, - mergeObjectWithArray(exceptionClass, args)); + return ((PreevaluatedExpression) signalExceptionNode).doPreEvaluated(frame, + SArguments.getPlainArguments(exceptionClass, args)); } - private Object[] mergeObjectWithArray(final Object o, final Object[] objects) { - Object[] allArgs = new Object[objects.length + 1]; - allArgs[0] = o; - for (int i = 0; i < objects.length; i++) { - allArgs[i + 1] = objects[i]; - } - return allArgs; - } } private static final class ResolveModule extends ExceptionSignalingNode { @@ -96,12 +90,12 @@ private ResolveModule(final SSymbol exceptionSelector, final SSymbol factorySele } @Override - public Object signal(final Object... args) { + public Object signal(VirtualFrame frame, final Object... args) { CompilerDirectives.transferToInterpreterAndInvalidate(); SObject module = resolver.get(); assert module != null : "Delayed lookup of module failed, still not available"; return replace(new ResolvedModule(module, exceptionSelector, factorySelector, - sourceSection)).signal(args); + sourceSection)).signal(frame, args); } } @@ -121,10 +115,10 @@ private LazyModule(final SSymbol exceptionSelector, final SSymbol factorySelecto } @Override - public Object signal(final Object... args) { + public Object signal(VirtualFrame frame, final Object... args) { CompilerDirectives.transferToInterpreterAndInvalidate(); return replace(new ResolvedModule(module, exceptionSelector, factorySelector, - sourceSection)).signal(args); + sourceSection)).signal(frame, args); } } } diff --git a/src/som/interpreter/nodes/InstantiationNode.java b/src/som/interpreter/nodes/InstantiationNode.java index 7c51435132..0d7a9ebe2a 100644 --- a/src/som/interpreter/nodes/InstantiationNode.java +++ b/src/som/interpreter/nodes/InstantiationNode.java @@ -3,6 +3,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import som.compiler.MixinDefinition; @@ -47,19 +48,19 @@ public static SClass instantiateMetaclassClass(final ClassFactory factory, } public static SClass signalExceptionsIfFaultFoundElseReturnClassObject( - final SObjectWithClass outerObj, - final ClassFactory factory, final SClass classObj, - final ExceptionSignalingNode notAValue, final ExceptionSignalingNode cannotBeValue) { + final VirtualFrame frame, final SObjectWithClass outerObj, final ClassFactory factory, + final SClass classObj, final ExceptionSignalingNode notAValue, + final ExceptionSignalingNode cannotBeValue) { factory.initializeClass(classObj); if (factory.isDeclaredAsValue() && factory.isTransferObject()) { // REM: cast is fine here, because we never return anyway - cannotBeValue.signal(classObj); + cannotBeValue.signal(frame, classObj); } if ((factory.isTransferObject() || factory.isDeclaredAsValue()) && !outerObj.isValue()) { - notAValue.signal(classObj); + notAValue.signal(frame, classObj); } return classObj; @@ -71,28 +72,31 @@ public ClassInstantiationNode(final MixinDefinition mixinDefinition) { super(mixinDefinition); } - public abstract SClass execute(SObjectWithClass outerObj, Object superclassAndMixins); + public abstract SClass execute(VirtualFrame frame, SObjectWithClass outerObj, + Object superclassAndMixins); @Specialization(guards = {"sameSuperAndMixins(superclassAndMixins, cachedSuperMixins)"}) - public SClass instantiateClass(final SObjectWithClass outerObj, + public SClass instantiateClass(final VirtualFrame frame, final SObjectWithClass outerObj, final Object superclassAndMixins, @Cached("superclassAndMixins") final Object cachedSuperMixins, @Cached("createClassFactory(superclassAndMixins)") final ClassFactory factory) { - return instantiate(outerObj, factory, notAValue, cannotBeValues); + return instantiate(frame, outerObj, factory, notAValue, cannotBeValues); } - public static SClass instantiate(final SObjectWithClass outerObj, + public static SClass instantiate(final VirtualFrame frame, final SObjectWithClass outerObj, final ClassFactory factory, final ExceptionSignalingNode notAValue, final ExceptionSignalingNode cannotBeValues) { SClass classObj = new SClass(outerObj, instantiateMetaclassClass(factory, outerObj)); - return signalExceptionsIfFaultFoundElseReturnClassObject(outerObj, factory, classObj, + return signalExceptionsIfFaultFoundElseReturnClassObject(frame, outerObj, factory, + classObj, notAValue, cannotBeValues); } @Specialization(replaces = "instantiateClass") - public SClass instantiateClassWithNewClassFactory(final SObjectWithClass outerObj, + public SClass instantiateClassWithNewClassFactory(final VirtualFrame frame, + final SObjectWithClass outerObj, final Object superclassAndMixins) { - return instantiateClass(outerObj, superclassAndMixins, null, + return instantiateClass(frame, outerObj, superclassAndMixins, null, createClassFactory(superclassAndMixins)); } } @@ -127,7 +131,8 @@ private static SClass instantiate(final SObjectWithClass outerObj, final InstantiationNode inst) { SClass classObj = new SClass(outerObj, instantiateMetaclassClass(factory, outerObj), frame); - return signalExceptionsIfFaultFoundElseReturnClassObject(outerObj, factory, classObj, + return signalExceptionsIfFaultFoundElseReturnClassObject(frame, outerObj, factory, + classObj, inst.notAValue, inst.cannotBeValues); } diff --git a/src/som/interpreter/nodes/InternalObjectArrayNode.java b/src/som/interpreter/nodes/InternalObjectArrayNode.java index b513c4ca77..0a19bdd0b2 100644 --- a/src/som/interpreter/nodes/InternalObjectArrayNode.java +++ b/src/som/interpreter/nodes/InternalObjectArrayNode.java @@ -5,12 +5,16 @@ import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; +import som.interpreter.SArguments; import som.interpreter.nodes.nary.ExprWithTagsNode; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; @NodeInfo(cost = NodeCost.NONE) -public final class InternalObjectArrayNode extends ExprWithTagsNode { - @Children private final ExpressionNode[] expressions; +public class InternalObjectArrayNode extends ExprWithTagsNode { + @Children protected final ExpressionNode[] expressions; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = + ShadowStackEntryLoad.create(); public InternalObjectArrayNode(final ExpressionNode[] expressions) { this.expressions = expressions; @@ -27,7 +31,27 @@ public Object[] executeObjectArray(final VirtualFrame frame) { } @Override - public Object executeGeneric(final VirtualFrame frame) { + public final Object executeGeneric(final VirtualFrame frame) { return executeObjectArray(frame); } + + public static class ArgumentEvaluationNode + extends InternalObjectArrayNode { + + public ArgumentEvaluationNode(final ExpressionNode[] expressions) { + super(expressions); + } + + @Override + @ExplodeLoop + public Object[] executeObjectArray(final VirtualFrame frame) { + Object[] values = SArguments.allocateArgumentsArray(expressions); + for (int i = 0; i < expressions.length; i++) { + values[i] = expressions[i].executeGeneric(frame); + } + SArguments.setShadowStackEntryWithCache(values, this, shadowStackEntryLoad, frame, + true); + return values; + } + } } diff --git a/src/som/interpreter/nodes/IsValueCheckNode.java b/src/som/interpreter/nodes/IsValueCheckNode.java index 49288c099d..fdf0aea05e 100644 --- a/src/som/interpreter/nodes/IsValueCheckNode.java +++ b/src/som/interpreter/nodes/IsValueCheckNode.java @@ -122,18 +122,18 @@ public Object allFieldsAreValues(final VirtualFrame frame, final SImmutableObjec if (cachedTester.allFieldsContainValues(rcvr)) { return rcvr; } else { - return notAValue.signal(rcvr); + return notAValue.signal(frame, rcvr); } } @Specialization - public Object fallback(final Object receiver) { + public Object fallback(final VirtualFrame frame, final Object receiver) { SImmutableObject rcvr = (SImmutableObject) receiver; boolean allFieldsContainValues = allFieldsContainValues(rcvr); if (allFieldsContainValues) { return rcvr; } - return notAValue.signal(rcvr); + return notAValue.signal(frame, rcvr); } protected static final class FieldTester extends Node { diff --git a/src/som/interpreter/nodes/MessageSendNode.java b/src/som/interpreter/nodes/MessageSendNode.java index 640f0f8aff..f78fc5b8f1 100644 --- a/src/som/interpreter/nodes/MessageSendNode.java +++ b/src/som/interpreter/nodes/MessageSendNode.java @@ -24,6 +24,7 @@ import som.VM; import som.compiler.AccessModifier; import som.interpreter.Invokable; +import som.interpreter.SArguments; import som.interpreter.TruffleCompiler; import som.interpreter.actors.ReceivedMessage; import som.interpreter.nodes.dispatch.AbstractDispatchNode; @@ -35,7 +36,9 @@ import som.primitives.reflection.AbstractSymbolDispatch; import som.vm.NotYetImplementedException; import som.vm.Primitives; +import som.vm.VmSettings; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.dym.Tags.VirtualInvoke; import tools.dym.profiles.DispatchProfile; @@ -156,12 +159,21 @@ public Object executeGeneric(final VirtualFrame frame) { @ExplodeLoop private Object[] evaluateArguments(final VirtualFrame frame) { - Object[] arguments = new Object[argumentNodes.length]; + Object[] arguments = SArguments.allocateArgumentsArray(argumentNodes); for (int i = 0; i < argumentNodes.length; i++) { arguments[i] = argumentNodes[i].executeGeneric(frame); - assert arguments[i] != null - : "Some expression evaluated to null, which is not supported."; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + && arguments[i] instanceof ShadowStackEntry) { + CompilerDirectives.transferToInterpreter(); + insert(ExceptionSignalingNode.createArgumentErrorNode(sourceSection)).signal(frame, + frame.getArguments()); + } + assert arguments[i] != null : "Some expression evaluated to null, which is not supported."; + assert !(arguments[i] instanceof ShadowStackEntry); } + // We allocate room for the arguments, but it is not set if non + // SArguments.setShadowStackEntryWithCache(arguments, this, shadowStackEntryLoad, frame, + // false); return arguments; } diff --git a/src/som/interpreter/nodes/ReturnNonLocalNode.java b/src/som/interpreter/nodes/ReturnNonLocalNode.java index 2d49b983c3..a641f1e2d4 100644 --- a/src/som/interpreter/nodes/ReturnNonLocalNode.java +++ b/src/som/interpreter/nodes/ReturnNonLocalNode.java @@ -94,7 +94,8 @@ public Object executeGeneric(final VirtualFrame frame) { SInvokable invokable = (SInvokable) Types.getClassOf(self).lookupMessage( escapedBlock, AccessModifier.PROTECTED); - return invokable.invoke(call, new Object[] {self, block}); + return invokable.invoke(call, + new Object[] {self, block, SArguments.getShadowStackEntry(frame)}); } } diff --git a/src/som/interpreter/nodes/SOMNode.java b/src/som/interpreter/nodes/SOMNode.java index 93547e8aca..f057bcc361 100644 --- a/src/som/interpreter/nodes/SOMNode.java +++ b/src/som/interpreter/nodes/SOMNode.java @@ -126,8 +126,7 @@ public static T unwrapIfNecessary(final T node) { } public static Node getParentIgnoringWrapper(final Node node) { - assert !(node instanceof WrapperNode) - : "A correct usage will not see nodes that are wrappers. This is to detect bugs"; + assert !(node instanceof WrapperNode) : "A correct usage will not see nodes that are wrappers. This is to detect bugs"; Node parent = node.getParent(); if (parent instanceof WrapperNode) { diff --git a/src/som/interpreter/nodes/dispatch/AbstractGenericDispatchNode.java b/src/som/interpreter/nodes/dispatch/AbstractGenericDispatchNode.java index 69618af937..97a34a4f51 100644 --- a/src/som/interpreter/nodes/dispatch/AbstractGenericDispatchNode.java +++ b/src/som/interpreter/nodes/dispatch/AbstractGenericDispatchNode.java @@ -22,11 +22,13 @@ import som.vmobjects.SArray; import som.vmobjects.SClass; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; public abstract class AbstractGenericDispatchNode extends AbstractDispatchNode { - @Child protected IndirectCallNode call; - protected final SSymbol selector; + @Child protected IndirectCallNode call; + protected final SSymbol selector; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); public AbstractGenericDispatchNode(final SourceSection source, final SSymbol selector) { @@ -43,6 +45,13 @@ public final Object executeDispatch(final VirtualFrame frame, final Object[] arg SClass rcvrClass = Types.getClassOf(rcvr); Dispatchable method = doLookup(rcvrClass); + // Here we fall back to the slow case since megamorphic sends + // are just not present in benchmarks + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + SArguments.setShadowStackEntryWithCache(arguments, this, + shadowStackEntryLoad, frame, false); + } + if (method != null) { return method.invoke(call, arguments); } else { diff --git a/src/som/interpreter/nodes/dispatch/BackCacheCallNode.java b/src/som/interpreter/nodes/dispatch/BackCacheCallNode.java new file mode 100644 index 0000000000..627234d5b1 --- /dev/null +++ b/src/som/interpreter/nodes/dispatch/BackCacheCallNode.java @@ -0,0 +1,57 @@ +package som.interpreter.nodes.dispatch; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; + +import som.interpreter.Invokable; +import som.interpreter.Method; +import som.interpreter.SArguments; +import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntry; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; + + +public interface BackCacheCallNode { + + static void initializeUniqueCaller(final RootCallTarget methodCallTarget, + final BackCacheCallNode node) { + RootNode root = methodCallTarget.getRootNode(); + if (root instanceof Method) { + ((Method) root).setNewCaller(node); + } + } + + static void setShadowStackEntry(final VirtualFrame frame, + final boolean uniqueCaller, final Object[] arguments, + final Node expression, + final ShadowStackEntryLoad shadowStackEntryLoad) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + assert arguments[arguments.length - 1] == null; + assert (frame.getArguments()[frame.getArguments().length + - 1] instanceof ShadowStackEntry); + assert frame.getArguments().length >= 2; + } + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_METHOD_CACHE) { + if (uniqueCaller) { + SArguments.setShadowStackEntry(arguments, SArguments.getShadowStackEntry(frame)); + } else { + SArguments.setShadowStackEntryWithCache(arguments, expression, + shadowStackEntryLoad, frame, false); + } + } else if (VmSettings.ACTOR_ASYNC_STACK_TRACE_INLINE_CACHE) { + SArguments.setShadowStackEntryWithCache(arguments, expression, + shadowStackEntryLoad, frame, false); + } + assert arguments[arguments.length - 1] != null + || (frame.getArguments()[frame.getArguments().length - 1] == null) + || !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE; + } + + void makeUniqueCaller(); + + void makeMultipleCaller(); + + Invokable getCachedMethod(); +} diff --git a/src/som/interpreter/nodes/dispatch/BlockDispatchNode.java b/src/som/interpreter/nodes/dispatch/BlockDispatchNode.java index 3c3d6be2dc..ab4857a15b 100644 --- a/src/som/interpreter/nodes/dispatch/BlockDispatchNode.java +++ b/src/som/interpreter/nodes/dispatch/BlockDispatchNode.java @@ -10,10 +10,17 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.frame.VirtualFrame; +import som.VM; import som.interpreter.Invokable; +import som.interpreter.SArguments; +import som.vm.VmSettings; import som.vmobjects.SBlock; import som.vmobjects.SInvokable; +import tools.debugger.asyncstacktraces.ShadowStackEntry; + +import java.util.Arrays; public abstract class BlockDispatchNode extends Node { @@ -21,7 +28,7 @@ public abstract class BlockDispatchNode extends Node { @CompilationFinal private boolean initialized; @CompilationFinal private boolean isAtomic; - public abstract Object executeDispatch(Object[] arguments); + public abstract Object executeDispatch(VirtualFrame frame, Object[] arguments); protected static final boolean isSameMethod(final Object[] arguments, final SInvokable cached) { @@ -33,7 +40,9 @@ protected static final boolean isSameMethod(final Object[] arguments, protected static final SInvokable getMethod(final Object[] arguments) { SInvokable method = ((SBlock) arguments[0]).getMethod(); - assert method.getNumberOfArguments() == arguments.length; + assert method.getNumberOfArguments() == arguments.length + || (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + && (method.getNumberOfArguments() == arguments.length - 1)); return method; } @@ -56,9 +65,15 @@ protected static final IndirectCallNode createIndirectCall() { } @Specialization(guards = "isSameMethod(arguments, cached)") - public Object activateCachedBlock(final Object[] arguments, + public Object activateCachedBlock(VirtualFrame frame, final Object[] arguments, @Cached("getMethod(arguments)") final SInvokable cached, @Cached("createCallNode(arguments)") final DirectCallNode call) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + && !(arguments[arguments.length - 1] instanceof ShadowStackEntry)) { + Object[] newArguments = Arrays.copyOf(arguments, arguments.length + 1); + newArguments[arguments.length] = SArguments.getShadowStackEntry(frame); + return call.call(newArguments); + } return call.call(arguments); } diff --git a/src/som/interpreter/nodes/dispatch/CachedDispatchNode.java b/src/som/interpreter/nodes/dispatch/CachedDispatchNode.java index 05c4182b9f..4a5cbe4259 100644 --- a/src/som/interpreter/nodes/dispatch/CachedDispatchNode.java +++ b/src/som/interpreter/nodes/dispatch/CachedDispatchNode.java @@ -2,47 +2,95 @@ import java.util.Map; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.impl.DefaultCallTarget; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.InvalidAssumptionException; import som.instrumentation.CountingDirectCallNode; import som.interpreter.Invokable; import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; -public final class CachedDispatchNode extends AbstractDispatchNode { - +public final class CachedDispatchNode extends AbstractDispatchNode + implements BackCacheCallNode { + private final Assumption stillUniqueCaller; @Child private DirectCallNode cachedMethod; @Child private AbstractDispatchNode nextInCache; + @CompilationFinal private boolean uniqueCaller; + + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); private final DispatchGuard guard; public CachedDispatchNode(final CallTarget methodCallTarget, final DispatchGuard guard, final AbstractDispatchNode nextInCache) { + this(methodCallTarget, guard, nextInCache, true); + } + + public CachedDispatchNode(final CallTarget methodCallTarget, + final DispatchGuard guard, final AbstractDispatchNode nextInCache, + final boolean defaultUniqueCaller) { super(nextInCache.getSourceSection()); + stillUniqueCaller = Truffle.getRuntime().createAssumption(); this.guard = guard; this.nextInCache = nextInCache; this.cachedMethod = Truffle.getRuntime().createDirectCallNode(methodCallTarget); if (VmSettings.DYNAMIC_METRICS) { this.cachedMethod = new CountingDirectCallNode(this.cachedMethod); } + if (defaultUniqueCaller) { + BackCacheCallNode.initializeUniqueCaller((RootCallTarget) methodCallTarget, this); + } + } + + public CachedDispatchNode(final CachedDispatchNode node, final boolean uniqueCaller) { + this(node.cachedMethod.getCallTarget(), node.guard, node.nextInCache, false); + this.uniqueCaller = uniqueCaller; + } + + @Override + public void makeUniqueCaller() { + uniqueCaller = true; + } + + @Override + public void makeMultipleCaller() { + uniqueCaller = false; + stillUniqueCaller.invalidate(); + } + + @Override + public Invokable getCachedMethod() { + RootCallTarget ct = (DefaultCallTarget) cachedMethod.getCallTarget(); + return (Invokable) ct.getRootNode(); } @Override public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) { try { if (guard.entryMatches(arguments[0])) { + stillUniqueCaller.check(); + BackCacheCallNode.setShadowStackEntry(frame, + uniqueCaller, arguments, this, shadowStackEntryLoad); return cachedMethod.call(arguments); } else { return nextInCache.executeDispatch(frame, arguments); } } catch (InvalidAssumptionException e) { CompilerDirectives.transferToInterpreterAndInvalidate(); - return replace(nextInCache).executeDispatch(frame, arguments); + if (stillUniqueCaller.isValid()) { + return replace(nextInCache).executeDispatch(frame, arguments); + } else { + return replace(new CachedDispatchNode(this, false)).executeDispatch(frame, arguments); + } } } diff --git a/src/som/interpreter/nodes/dispatch/CachedDnuNode.java b/src/som/interpreter/nodes/dispatch/CachedDnuNode.java index bc81e129cc..7ed60532df 100644 --- a/src/som/interpreter/nodes/dispatch/CachedDnuNode.java +++ b/src/som/interpreter/nodes/dispatch/CachedDnuNode.java @@ -19,9 +19,11 @@ import som.primitives.SystemPrims.PrintStackTracePrim; import som.vm.Symbols; import som.vm.VmSettings; +import som.vmobjects.SArray; import som.vmobjects.SClass; import som.vmobjects.SInvokable; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; public final class CachedDnuNode extends AbstractDispatchNode { @@ -32,6 +34,8 @@ public final class CachedDnuNode extends AbstractDispatchNode { private final DispatchGuard guard; private final SSymbol selector; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + public CachedDnuNode(final SClass rcvrClass, final SSymbol selector, final DispatchGuard guard, final VM vm, final AbstractDispatchNode nextInCache) { @@ -47,6 +51,13 @@ public CachedDnuNode(final SClass rcvrClass, final SSymbol selector, public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) { boolean match; Object rcvr = arguments[0]; + // Here we fall back to the slow case since DNU sends + // are just too uncommon and we don't want to recreate + // the stack across DNUs + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + SArguments.setShadowStackEntryWithCache(arguments, this, + shadowStackEntryLoad, frame, false); + } try { match = guard.entryMatches(rcvr); } catch (InvalidAssumptionException e) { @@ -66,9 +77,17 @@ protected Object performDnu(final Object[] arguments, Output.errorPrintln("Lookup of " + selector + " failed in " + Types.getClassOf(rcvr).getName().getString()); } - - Object[] argsArr = new Object[] { - rcvr, selector, SArguments.getArgumentsWithoutReceiver(arguments)}; + Object[] argsArr; + SArray.SImmutableArray dnuArguments = SArguments.getArgumentsWithoutReceiver(arguments); + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + argsArr = new Object[] { + rcvr, selector, dnuArguments, + arguments[arguments.length - 1] + }; + } else { + argsArr = new Object[] { + rcvr, selector, dnuArguments}; + } return cachedMethod.call(argsArr); } diff --git a/src/som/interpreter/nodes/dispatch/CachedSlotRead.java b/src/som/interpreter/nodes/dispatch/CachedSlotRead.java index 6c6da5997b..c0590c17bf 100644 --- a/src/som/interpreter/nodes/dispatch/CachedSlotRead.java +++ b/src/som/interpreter/nodes/dispatch/CachedSlotRead.java @@ -11,6 +11,7 @@ import com.oracle.truffle.api.profiles.IntValueProfile; import som.interpreter.Invokable; +import som.interpreter.SArguments; import som.interpreter.nodes.dispatch.DispatchGuard.CheckSObject; import som.interpreter.objectstorage.StorageAccessor.AbstractObjectAccessor; import som.interpreter.objectstorage.StorageAccessor.AbstractPrimitiveAccessor; @@ -61,7 +62,7 @@ protected CachedSlotRead() { public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) { try { if (guard.entryMatches(arguments[0])) { - return read(guard.cast(arguments[0])); + return read(frame, guard.cast(arguments[0]), SArguments.getShadowStackEntry(frame)); } else { return nextInCache.executeDispatch(frame, arguments); } @@ -71,7 +72,11 @@ public Object executeDispatch(final VirtualFrame frame, final Object[] arguments } } - public abstract Object read(SObject rcvr); + public abstract Object read(VirtualFrame frame, SObject rcvr); + + public Object read(final VirtualFrame frame, final SObject rcvr, final Object maybeEntry) { + return read(frame, rcvr); + } @Override public int lengthOfDispatchChain() { @@ -116,7 +121,7 @@ public UnwrittenSlotRead(final SlotAccess type, final CheckSObject guard, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { return Nil.nilObject; } } @@ -132,7 +137,7 @@ public ObjectSlotRead(final AbstractObjectAccessor accessor, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { return accessor.read(rcvr); } } @@ -159,7 +164,7 @@ public LongSlotReadSetOrUnset(final AbstractPrimitiveAccessor accessor, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { if (accessor.isPrimitiveSet(rcvr, primMarkProfile)) { return accessor.readLong(rcvr); } else { @@ -177,7 +182,7 @@ public LongSlotReadSet(final AbstractPrimitiveAccessor accessor, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { if (accessor.isPrimitiveSet(rcvr, primMarkProfile)) { return accessor.readLong(rcvr); } else { @@ -197,7 +202,7 @@ public DoubleSlotReadSetOrUnset(final AbstractPrimitiveAccessor accessor, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { if (accessor.isPrimitiveSet(rcvr, primMarkProfile)) { return accessor.readDouble(rcvr); } else { @@ -215,7 +220,7 @@ public DoubleSlotReadSet(final AbstractPrimitiveAccessor accessor, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { if (accessor.isPrimitiveSet(rcvr, primMarkProfile)) { return accessor.readDouble(rcvr); } else { diff --git a/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java b/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java index 88aa2656c8..a556131cab 100644 --- a/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java +++ b/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java @@ -19,6 +19,7 @@ import som.vm.constants.Nil; import som.vmobjects.SClass; import som.vmobjects.SObject; +import tools.debugger.asyncstacktraces.ShadowStackEntry; /** @@ -57,11 +58,16 @@ public ClassSlotAccessNode(final MixinDefinition mixinDef, final SlotDefinition } @Override - public SClass read(final SObject rcvr) { + public SClass read(final VirtualFrame frame, final SObject rcvr) { + return this.read(frame, rcvr, null); + } + + @Override + public SClass read(final VirtualFrame frame, final SObject rcvr, final Object maybeEntry) { // here we need to synchronize, because this is actually something that // can happen concurrently, and we only want a single instance of the // class object - Object cachedValue = read.read(rcvr); + Object cachedValue = read.read(frame, rcvr); assert cachedValue != null; if (cachedValue != Nil.nilObject) { @@ -83,7 +89,7 @@ public SClass read(final SObject rcvr) { // recheck guard under synchronized, don't want to access object if // layout might have changed, we are going to slow path in that case guard.entryMatches(rcvr); - cachedValue = read.read(rcvr); + cachedValue = read.read(frame, rcvr); } catch (InvalidAssumptionException e) { CompilerDirectives.transferToInterpreterAndInvalidate(); cachedValue = rcvr.readSlot(slotDef); @@ -91,7 +97,7 @@ public SClass read(final SObject rcvr) { // check whether cache is initialized with class object if (cachedValue == Nil.nilObject) { - return instantiateAndWriteUnsynced(rcvr); + return instantiateAndWriteUnsynced(frame, rcvr, maybeEntry); } else { assert cachedValue instanceof SClass; return (SClass) cachedValue; @@ -102,8 +108,9 @@ public SClass read(final SObject rcvr) { /** * Caller needs to hold lock on {@code this}. */ - private SClass instantiateAndWriteUnsynced(final SObject rcvr) { - SClass classObject = instantiateClassObject(rcvr); + private SClass instantiateAndWriteUnsynced(final VirtualFrame frame, final SObject rcvr, + final Object maybeEntry) { + SClass classObject = instantiateClassObject(frame, rcvr, maybeEntry); try { // recheck guard under synchronized, don't want to access object if @@ -127,14 +134,33 @@ private void createResolverCallTargets() { invokable.getCallTarget())); } - private SClass instantiateClassObject(final SObject rcvr) { + private SClass instantiateClassObject(final VirtualFrame frame, final SObject rcvr, + final Object maybeEntry) { if (superclassAndMixinResolver == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); createResolverCallTargets(); } - Object superclassAndMixins = superclassAndMixinResolver.call(new Object[] {rcvr}); - SClass classObject = instantiation.execute(rcvr, superclassAndMixins); + Object superclassAndMixins; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // maybeEntry is set if coming from a cached dispatch node + // Not set if comes from elsewhere (materializer, etc.).. + // But it may not make sense. + ShadowStackEntry actualEntry; + if (maybeEntry != null) { + assert maybeEntry instanceof ShadowStackEntry; + actualEntry = (ShadowStackEntry) maybeEntry; + } else { + actualEntry = SArguments.instantiateTopShadowStackEntry(this); + } + superclassAndMixins = + superclassAndMixinResolver.call( + new Object[] {rcvr, actualEntry}); + } else { + superclassAndMixins = superclassAndMixinResolver.call(new Object[] {rcvr}); + } + + SClass classObject = instantiation.execute(frame, rcvr, superclassAndMixins); return classObject; } } diff --git a/src/som/interpreter/nodes/dispatch/LexicallyBoundDispatchNode.java b/src/som/interpreter/nodes/dispatch/LexicallyBoundDispatchNode.java index 6c29a60e3c..ef120bf0ef 100644 --- a/src/som/interpreter/nodes/dispatch/LexicallyBoundDispatchNode.java +++ b/src/som/interpreter/nodes/dispatch/LexicallyBoundDispatchNode.java @@ -2,36 +2,83 @@ import java.util.Map; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.impl.DefaultCallTarget; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.source.SourceSection; import som.instrumentation.CountingDirectCallNode; import som.interpreter.Invokable; +import som.interpreter.Method; import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; /** * Private methods are special, they are linked unconditionally to the call site. * Thus, we don't need to check at the dispatch whether they apply or not. */ -public final class LexicallyBoundDispatchNode extends AbstractDispatchNode { +public abstract class LexicallyBoundDispatchNode extends AbstractDispatchNode + implements BackCacheCallNode { - @Child private DirectCallNode cachedMethod; + protected final Assumption stillUniqueCaller; + @Child private DirectCallNode cachedMethod; + @CompilationFinal protected boolean uniqueCaller; + + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); public LexicallyBoundDispatchNode(final SourceSection source, final CallTarget methodCallTarget) { super(source); + stillUniqueCaller = Truffle.getRuntime().createAssumption(); cachedMethod = Truffle.getRuntime().createDirectCallNode(methodCallTarget); if (VmSettings.DYNAMIC_METRICS) { this.cachedMethod = new CountingDirectCallNode(this.cachedMethod); } + BackCacheCallNode.initializeUniqueCaller((RootCallTarget) methodCallTarget, this); + } + + @Override + public void makeUniqueCaller() { + uniqueCaller = true; + } + + @Override + public void makeMultipleCaller() { + uniqueCaller = false; + stillUniqueCaller.invalidate(); + } + + public boolean isUniqueCaller(final VirtualFrame frame) { + return uniqueCaller; + } + + @Override + public Method getCachedMethod() { + RootCallTarget ct = (DefaultCallTarget) cachedMethod.getCallTarget(); + return (Method) ct.getRootNode(); } @Override - public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) { + public abstract Object executeDispatch(VirtualFrame frame, Object[] arguments); + + @Specialization(assumptions = "stillUniqueCaller", guards = "isUniqueCaller(frame)") + public Object uniqueCallerDispatch(final VirtualFrame frame, final Object[] arguments) { + BackCacheCallNode.setShadowStackEntry(frame, + true, arguments, this, shadowStackEntryLoad); + return cachedMethod.call(arguments); + } + + @Specialization(guards = "!isUniqueCaller(frame)") + public Object multipleCallerDispatch(final VirtualFrame frame, final Object[] arguments) { + BackCacheCallNode.setShadowStackEntry(frame, + false, arguments, this, shadowStackEntryLoad); return cachedMethod.call(arguments); } diff --git a/src/som/interpreter/nodes/literals/ArrayLiteralNode.java b/src/som/interpreter/nodes/literals/ArrayLiteralNode.java index 12b1b45b3b..671cd775f7 100644 --- a/src/som/interpreter/nodes/literals/ArrayLiteralNode.java +++ b/src/som/interpreter/nodes/literals/ArrayLiteralNode.java @@ -78,8 +78,7 @@ private void specialize(final SMutableArray storage) { } else if (storage.isLongType()) { node = new Longs(expressions); } else { - assert storage.isObjectType() - : "Partially empty is not supported yet. Should be simple to add."; + assert storage.isObjectType() : "Partially empty is not supported yet. Should be simple to add."; node = new Objects(expressions); } replace(node.initialize(sourceSection)); diff --git a/src/som/interpreter/nodes/literals/ObjectLiteralNode.java b/src/som/interpreter/nodes/literals/ObjectLiteralNode.java index 3510a10502..8b8c7ee72d 100644 --- a/src/som/interpreter/nodes/literals/ObjectLiteralNode.java +++ b/src/som/interpreter/nodes/literals/ObjectLiteralNode.java @@ -5,10 +5,12 @@ import bd.inlining.ScopeAdaptationVisitor; import som.compiler.MixinDefinition; +import som.interpreter.SArguments; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.InstantiationNode.ObjectLiteralInstantiationNode; import som.interpreter.nodes.InstantiationNodeFactory.ObjectLiteralInstantiationNodeGen; import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode; +import som.vm.VmSettings; import som.vmobjects.SClass; import som.vmobjects.SObjectWithClass; @@ -44,10 +46,23 @@ public ObjectLiteralNode(final MixinDefinition mixinDefiniton, @Override public Object executeGeneric(final VirtualFrame frame) { SObjectWithClass outer = (SObjectWithClass) outerRead.executeGeneric(frame); - Object superclassAndMixins = superClassResolver.call(outer); + + Object superclassAndMixins; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + superclassAndMixins = + superClassResolver.call(outer, SArguments.getShadowStackEntry(frame)); + } else { + superclassAndMixins = superClassResolver.call(outer); + } SClass sClassObject = instantiation.execute(outer, superclassAndMixins, frame.materialize()); - return newMessage.doPreEvaluated(frame, new Object[] {sClassObject}); + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {sClassObject, null}; + } else { + arguments = new Object[] {sClassObject}; + } + return newMessage.doPreEvaluated(frame, arguments); } @Override diff --git a/src/som/interpreter/nodes/nary/EagerBinaryPrimitiveNode.java b/src/som/interpreter/nodes/nary/EagerBinaryPrimitiveNode.java index 237912acf2..e11780c59e 100644 --- a/src/som/interpreter/nodes/nary/EagerBinaryPrimitiveNode.java +++ b/src/som/interpreter/nodes/nary/EagerBinaryPrimitiveNode.java @@ -10,6 +10,7 @@ import som.interpreter.nodes.MessageSendNode; import som.interpreter.nodes.MessageSendNode.GenericMessageSendNode; import som.vm.NotYetImplementedException; +import som.vm.VmSettings; import som.vmobjects.SSymbol; @@ -34,10 +35,9 @@ protected EagerlySpecializableNode getPrimitive() { @Override public boolean hasTag(final Class tag) { - assert !(primitive instanceof WrapperNode) - : "primitive can't be WrapperNodes to avoid double wrapping. It is: " - + primitive.getClass().getSimpleName() + " and contains a " - + ((WrapperNode) primitive).getDelegateNode().getClass().getSimpleName(); + assert !(primitive instanceof WrapperNode) : "primitive can't be WrapperNodes to avoid double wrapping. It is: " + + primitive.getClass().getSimpleName() + " and contains a " + + ((WrapperNode) primitive).getDelegateNode().getClass().getSimpleName(); return primitive.hasTagIgnoringEagerness(tag); } @@ -94,15 +94,28 @@ public Object executeGeneric(final VirtualFrame frame) { return executeEvaluated(frame, rcvr, arg); } + /** MATTEO: HERE IS THE SPOT IN WHICH I MIGHT BREAK EVERYTHING. **/ public Object executeEvaluated(final VirtualFrame frame, final Object receiver, final Object argument) { try { + // if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // Object[] args; + // + // args = new Object[] {receiver, argument, null}; + // + // return makeGenericSend().doPreEvaluated(frame, args); + // } return primitive.executeEvaluated(frame, receiver, argument); } catch (UnsupportedSpecializationException e) { TruffleCompiler.transferToInterpreterAndInvalidate( "Eager Primitive with unsupported specialization."); - return makeGenericSend().doPreEvaluated(frame, - new Object[] {receiver, argument}); + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {receiver, argument, null}; + } else { + args = new Object[] {receiver, argument}; + } + return makeGenericSend().doPreEvaluated(frame, args); } } @@ -130,8 +143,7 @@ protected void onReplace(final Node newNode, final CharSequence reason) { if (newNode instanceof ExprWithTagsNode) { ((ExprWithTagsNode) newNode).tagMark = primitive.tagMark; } else if (newNode instanceof WrapperNode) { - assert ((WrapperNode) newNode).getDelegateNode() == this - : "Wrapping should not also do specialization or other changes, I think"; + assert ((WrapperNode) newNode).getDelegateNode() == this : "Wrapping should not also do specialization or other changes, I think"; } else { throw new NotYetImplementedException(); } diff --git a/src/som/interpreter/nodes/nary/EagerTernaryPrimitiveNode.java b/src/som/interpreter/nodes/nary/EagerTernaryPrimitiveNode.java index dd9e30cbea..f9329a66cd 100644 --- a/src/som/interpreter/nodes/nary/EagerTernaryPrimitiveNode.java +++ b/src/som/interpreter/nodes/nary/EagerTernaryPrimitiveNode.java @@ -5,12 +5,14 @@ import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.Node; +import som.interpreter.SArguments; import som.interpreter.TruffleCompiler; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.MessageSendNode; import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode; import som.interpreter.nodes.MessageSendNode.GenericMessageSendNode; import som.vm.NotYetImplementedException; +import som.vm.VmSettings; import som.vmobjects.SSymbol; @@ -38,8 +40,7 @@ protected EagerlySpecializableNode getPrimitive() { @Override public boolean hasTag(final Class tag) { - assert !(primitive instanceof WrapperNode) - : "Eager primitives are expected to point directly to primitive nodes, and do not have wrapper nodes. I think, we wanted the wrapper nodes to be strictly around the eager wrappers."; + assert !(primitive instanceof WrapperNode) : "Eager primitives are expected to point directly to primitive nodes, and do not have wrapper nodes. I think, we wanted the wrapper nodes to be strictly around the eager wrappers."; return primitive.hasTagIgnoringEagerness(tag); } @@ -100,12 +101,19 @@ public Object executeGeneric(final VirtualFrame frame) { public Object executeEvaluated(final VirtualFrame frame, final Object receiver, final Object argument1, final Object argument2) { try { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + primitive.maybeEntry = SArguments.getShadowStackEntry(frame); + } return primitive.executeEvaluated(frame, receiver, argument1, argument2); } catch (UnsupportedSpecializationException e) { TruffleCompiler.transferToInterpreterAndInvalidate( "Eager Primitive with unsupported specialization."); + Object[] arguments = {receiver, argument1, argument2}; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {receiver, argument1, argument2, null}; + } return makeGenericSend().doPreEvaluated(frame, - new Object[] {receiver, argument1, argument2}); + arguments); } } @@ -133,8 +141,7 @@ protected void onReplace(final Node newNode, final CharSequence reason) { if (newNode instanceof ExprWithTagsNode) { ((ExprWithTagsNode) newNode).tagMark = primitive.tagMark; } else if (newNode instanceof WrapperNode) { - assert ((WrapperNode) newNode).getDelegateNode() == this - : "Wrapping should not also do specialization or other changes, I think"; + assert ((WrapperNode) newNode).getDelegateNode() == this : "Wrapping should not also do specialization or other changes, I think"; } else { throw new NotYetImplementedException(); } diff --git a/src/som/interpreter/nodes/nary/EagerUnaryPrimitiveNode.java b/src/som/interpreter/nodes/nary/EagerUnaryPrimitiveNode.java index 6bf7507282..2335cfdba6 100644 --- a/src/som/interpreter/nodes/nary/EagerUnaryPrimitiveNode.java +++ b/src/som/interpreter/nodes/nary/EagerUnaryPrimitiveNode.java @@ -5,11 +5,13 @@ import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.Node; +import som.interpreter.SArguments; import som.interpreter.TruffleCompiler; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.MessageSendNode; import som.interpreter.nodes.MessageSendNode.GenericMessageSendNode; import som.vm.NotYetImplementedException; +import som.vm.VmSettings; import som.vmobjects.SSymbol; @@ -94,7 +96,13 @@ public Object executeEvaluated(final VirtualFrame frame, } catch (UnsupportedSpecializationException e) { TruffleCompiler.transferToInterpreterAndInvalidate( "Eager Primitive with unsupported specialization."); - return makeGenericSend().doPreEvaluated(frame, new Object[] {receiver}); + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {receiver, null}; + } else { + arguments = new Object[] {receiver}; + } + return makeGenericSend().doPreEvaluated(frame, arguments); } } @@ -122,8 +130,7 @@ protected void onReplace(final Node newNode, final CharSequence reason) { if (newNode instanceof ExprWithTagsNode) { ((ExprWithTagsNode) newNode).tagMark = primitive.tagMark; } else if (newNode instanceof WrapperNode) { - assert ((WrapperNode) newNode).getDelegateNode() == this - : "Wrapping should not also do specialization or other changes, I think"; + assert ((WrapperNode) newNode).getDelegateNode() == this : "Wrapping should not also do specialization or other changes, I think"; } else { throw new NotYetImplementedException(); } diff --git a/src/som/interpreter/nodes/nary/TernaryExpressionNode.java b/src/som/interpreter/nodes/nary/TernaryExpressionNode.java index fd4c996b6a..69df44a6ac 100644 --- a/src/som/interpreter/nodes/nary/TernaryExpressionNode.java +++ b/src/som/interpreter/nodes/nary/TernaryExpressionNode.java @@ -11,6 +11,7 @@ import som.VM; import som.interpreter.nodes.ExpressionNode; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntry; @NodeChildren({ @@ -20,6 +21,8 @@ @GenerateWrapper public abstract class TernaryExpressionNode extends EagerlySpecializableNode { + protected ShadowStackEntry maybeEntry; + protected TernaryExpressionNode() {} protected TernaryExpressionNode(final TernaryExpressionNode wrappedNode) {} diff --git a/src/som/interpreter/nodes/specialized/IfMessageNode.java b/src/som/interpreter/nodes/specialized/IfMessageNode.java index cf4bae1d1e..b8d0704724 100644 --- a/src/som/interpreter/nodes/specialized/IfMessageNode.java +++ b/src/som/interpreter/nodes/specialized/IfMessageNode.java @@ -7,9 +7,12 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.frame.VirtualFrame; import bd.primitives.Primitive; +import som.interpreter.SArguments; import som.interpreter.nodes.nary.BinaryComplexOperation; +import som.vm.VmSettings; import som.vm.constants.Nil; import som.vmobjects.SBlock; import som.vmobjects.SInvokable; @@ -49,14 +52,22 @@ protected static IndirectCallNode createIndirect() { } @Specialization(guards = {"arg.getMethod() == method"}) - public final Object cachedBlock(final boolean rcvr, final SBlock arg, + public final Object cachedBlock(VirtualFrame frame, final boolean rcvr, final SBlock arg, @Cached("arg.getMethod()") final SInvokable method, @Cached("createDirect(method)") final DirectCallNode callTarget) { + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {arg, SArguments.getShadowStackEntry(frame)}; + } else { + args = new Object[] {arg}; + } + // this was there before Async Stack traces if (condProf.profile(rcvr == expected)) { - return callTarget.call(new Object[] {arg}); + return callTarget.call(args); } else { return Nil.nilObject; } + } @Specialization(replaces = "cachedBlock") diff --git a/src/som/interpreter/nodes/specialized/IntDownToDoMessageNode.java b/src/som/interpreter/nodes/specialized/IntDownToDoMessageNode.java index 06aa5fea67..a48614bc3b 100644 --- a/src/som/interpreter/nodes/specialized/IntDownToDoMessageNode.java +++ b/src/som/interpreter/nodes/specialized/IntDownToDoMessageNode.java @@ -3,35 +3,40 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import bd.primitives.Primitive; import som.interpreter.nodes.specialized.IntToDoMessageNode.ToDoSplzr; import som.vmobjects.SBlock; import som.vmobjects.SInvokable; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; @GenerateNodeFactory @Primitive(selector = "downTo:do:", noWrapper = true, disabled = true, specializer = ToDoSplzr.class) public abstract class IntDownToDoMessageNode extends IntToDoMessageNode { + + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + @Override @Specialization(guards = "block.getMethod() == blockMethod") - public final long doIntToDo(final long receiver, + public final long doIntToDo(final VirtualFrame frame, final long receiver, final long limit, final SBlock block, @Cached("block.getMethod()") final SInvokable blockMethod, @Cached("create(blockMethod)") final DirectCallNode valueSend) { - return IntToByDoMessageNode.doLoop(valueSend, this, receiver, - limit, -1, block); + return IntToByDoMessageNode.doLoop(frame, valueSend, this, receiver, + limit, -1, block, shadowStackEntryLoad); } @Override @Specialization(guards = "block.getMethod() == blockMethod") - public final long doIntToDo(final long receiver, + public final long doIntToDo(final VirtualFrame frame, final long receiver, final double dLimit, final SBlock block, @Cached("block.getMethod()") final SInvokable blockMethod, @Cached("create(blockMethod)") final DirectCallNode valueSend) { - return IntToByDoMessageNode.doLoop(valueSend, this, receiver, - (long) dLimit, -1, block); + return IntToByDoMessageNode.doLoop(frame, valueSend, this, receiver, + (long) dLimit, -1, block, shadowStackEntryLoad); } } diff --git a/src/som/interpreter/nodes/specialized/IntToByDoMessageNode.java b/src/som/interpreter/nodes/specialized/IntToByDoMessageNode.java index 4a77fd90c3..0907551ccf 100644 --- a/src/som/interpreter/nodes/specialized/IntToByDoMessageNode.java +++ b/src/som/interpreter/nodes/specialized/IntToByDoMessageNode.java @@ -4,16 +4,18 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.DirectCallNode; -import com.oracle.truffle.api.nodes.Node; import bd.primitives.Primitive; +import som.interpreter.SArguments; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.nary.QuaternaryExpressionNode; import som.interpreter.objectstorage.ObjectTransitionSafepoint; import som.vmobjects.SBlock; import som.vmobjects.SInvokable; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; import tools.dym.Tags.LoopNode; @@ -23,6 +25,8 @@ public abstract class IntToByDoMessageNode extends QuaternaryExpressionNode { protected final SInvokable blockMethod; @Child protected DirectCallNode valueSend; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + public IntToByDoMessageNode(final Object[] args) { blockMethod = ((SBlock) args[3]).getMethod(); valueSend = Truffle.getRuntime().createDirectCallNode(blockMethod.getCallTarget()); @@ -38,26 +42,30 @@ protected boolean hasTagIgnoringEagerness(final Class tag) { } @Specialization(guards = "block.getMethod() == blockMethod") - public final long doIntToByDo(final long receiver, + public final long doIntToByDo(final VirtualFrame frame, final long receiver, final long limit, final long step, final SBlock block) { - return doLoop(valueSend, this, receiver, limit, step, block); + return doLoop(frame, valueSend, this, receiver, limit, step, block, shadowStackEntryLoad); } @Specialization(guards = "block.getMethod() == blockMethod") - public final long doIntToByDo(final long receiver, + public final long doIntToByDo(final VirtualFrame frame, final long receiver, final double limit, final long step, final SBlock block) { - return doLoop(valueSend, this, receiver, (long) limit, step, block); + return doLoop(frame, valueSend, this, receiver, (long) limit, step, block, + shadowStackEntryLoad); } - public static long doLoop(final DirectCallNode value, - final Node loopNode, final long receiver, final long limit, final long step, - final SBlock block) { + public static long doLoop(final VirtualFrame frame, final DirectCallNode value, + final ExpressionNode loopNode, final long receiver, final long limit, final long step, + final SBlock block, ShadowStackEntryLoad shadowStackEntryLoad) { try { if (receiver <= limit) { - value.call(new Object[] {block, receiver}); + value.call(SArguments.getPlainXArgumentsWithReceiver(loopNode, + shadowStackEntryLoad, frame, block, receiver)); } for (long i = receiver + step; i <= limit; i += step) { - value.call(new Object[] {block, i}); + value.call(SArguments.getPlainXArgumentsWithReceiver(loopNode, + shadowStackEntryLoad, frame, block, i)); + ObjectTransitionSafepoint.INSTANCE.checkAndPerformSafepoint(); } } finally { diff --git a/src/som/interpreter/nodes/specialized/IntToDoMessageNode.java b/src/som/interpreter/nodes/specialized/IntToDoMessageNode.java index 7f5d260ff9..edf3114380 100644 --- a/src/som/interpreter/nodes/specialized/IntToDoMessageNode.java +++ b/src/som/interpreter/nodes/specialized/IntToDoMessageNode.java @@ -5,6 +5,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.DirectCallNode; @@ -18,6 +19,7 @@ import som.vmobjects.SBlock; import som.vmobjects.SInvokable; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; import tools.dym.Tags.LoopNode; @@ -25,6 +27,9 @@ @Primitive(selector = "to:do:", noWrapper = true, disabled = true, specializer = ToDoSplzr.class, inParser = false) public abstract class IntToDoMessageNode extends TernaryExpressionNode { + + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + public static class ToDoSplzr extends Specializer { public ToDoSplzr(final Primitive prim, final NodeFactory fact) { super(prim, fact); @@ -52,20 +57,21 @@ protected boolean hasTagIgnoringEagerness(final Class tag) { } @Specialization(guards = "block.getMethod() == blockMethod") - public long doIntToDo(final long receiver, final long limit, final SBlock block, + public long doIntToDo(final VirtualFrame frame, final long receiver, final long limit, + final SBlock block, @Cached("block.getMethod()") final SInvokable blockMethod, @Cached("create(blockMethod)") final DirectCallNode valueSend) { - return IntToByDoMessageNode.doLoop(valueSend, this, receiver, - limit, 1, block); + return IntToByDoMessageNode.doLoop(frame, valueSend, this, receiver, + limit, 1, block, shadowStackEntryLoad); } @Specialization(guards = "block.getMethod() == blockMethod") - public long doIntToDo(final long receiver, final double dLimit, + public long doIntToDo(final VirtualFrame frame, final long receiver, final double dLimit, final SBlock block, @Cached("block.getMethod()") final SInvokable blockMethod, @Cached("create(blockMethod)") final DirectCallNode valueSend) { - return IntToByDoMessageNode.doLoop(valueSend, this, receiver, - (long) dLimit, 1, block); + return IntToByDoMessageNode.doLoop(frame, valueSend, this, receiver, + (long) dLimit, 1, block, shadowStackEntryLoad); } @Override diff --git a/src/som/interpreter/nodes/specialized/SomLoop.java b/src/som/interpreter/nodes/specialized/SomLoop.java index 9130aab158..781ab1ad91 100644 --- a/src/som/interpreter/nodes/specialized/SomLoop.java +++ b/src/som/interpreter/nodes/specialized/SomLoop.java @@ -1,11 +1,11 @@ package som.interpreter.nodes.specialized; -import som.interpreter.Invokable; - import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import som.interpreter.Invokable; + public abstract class SomLoop { diff --git a/src/som/interpreter/objectstorage/StorageAccessor.java b/src/som/interpreter/objectstorage/StorageAccessor.java index e23d88c9a2..a368aac250 100644 --- a/src/som/interpreter/objectstorage/StorageAccessor.java +++ b/src/som/interpreter/objectstorage/StorageAccessor.java @@ -59,18 +59,16 @@ private static long getFieldOffset(final String fieldName) { } public static AbstractObjectAccessor getObjectAccessor(final int idx) { - assert idx < MAX_OBJECT_FIELDS - : "Got a object slot allocated that goes beyond the currently supported. idx: " - + idx; + assert idx < MAX_OBJECT_FIELDS : "Got a object slot allocated that goes beyond the currently supported. idx: " + + idx; AbstractObjectAccessor result = objAccessors[idx]; assert result != null : "Object accessors not yet initialized?"; return result; } public static AbstractPrimitiveAccessor getPrimitiveAccessor(final int idx) { - assert idx < MAX_OBJECT_FIELDS - : "Got a primitive slot allocated that goes beyond the currently supported. idx: " - + idx; + assert idx < MAX_OBJECT_FIELDS : "Got a primitive slot allocated that goes beyond the currently supported. idx: " + + idx; AbstractPrimitiveAccessor result = primAccessors[idx]; assert result != null : "Primitive accessors not yet initialized?"; return result; diff --git a/src/som/interpreter/objectstorage/StorageLocation.java b/src/som/interpreter/objectstorage/StorageLocation.java index a933cddf07..6c2ecb8822 100644 --- a/src/som/interpreter/objectstorage/StorageLocation.java +++ b/src/som/interpreter/objectstorage/StorageLocation.java @@ -208,8 +208,7 @@ public void write(final SObject obj, final Object value) { @Override public boolean isSet(final SObject obj) { assert read( - obj) != null - : "null is not a valid value for an object slot, it needs to be initialized with nil."; + obj) != null : "null is not a valid value for an object slot, it needs to be initialized with nil."; return true; } diff --git a/src/som/interpreter/transactions/CachedTxSlotRead.java b/src/som/interpreter/transactions/CachedTxSlotRead.java index a08e10b811..3a2af5386b 100644 --- a/src/som/interpreter/transactions/CachedTxSlotRead.java +++ b/src/som/interpreter/transactions/CachedTxSlotRead.java @@ -1,5 +1,7 @@ package som.interpreter.transactions; +import com.oracle.truffle.api.frame.VirtualFrame; + import som.interpreter.nodes.dispatch.AbstractDispatchNode; import som.interpreter.nodes.dispatch.CachedSlotRead; import som.interpreter.nodes.dispatch.DispatchGuard.CheckSObject; @@ -19,8 +21,8 @@ public CachedTxSlotRead(final SlotAccess type, } @Override - public Object read(final SObject rcvr) { + public Object read(final VirtualFrame frame, final SObject rcvr) { SMutableObject workingCopy = Transactions.workingCopy((SMutableObject) rcvr); - return read.read(workingCopy); + return read.read(frame, workingCopy); } } diff --git a/src/som/primitives/ActivitySpawn.java b/src/som/primitives/ActivitySpawn.java index ffa61763fe..4746533423 100644 --- a/src/som/primitives/ActivitySpawn.java +++ b/src/som/primitives/ActivitySpawn.java @@ -1,7 +1,9 @@ package som.primitives; +import java.util.Arrays; import java.util.concurrent.ForkJoinPool; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -16,6 +18,7 @@ import bd.primitives.Primitive; import som.VM; +import som.interpreter.SArguments; import som.interpreter.nodes.ExceptionSignalingNode; import som.interpreter.nodes.nary.BinaryComplexOperation.BinarySystemOperation; import som.interpreter.nodes.nary.TernaryExpressionNode.TernarySystemOperation; @@ -45,10 +48,11 @@ import tools.concurrency.KomposTrace; import tools.concurrency.Tags.ActivityCreation; import tools.concurrency.Tags.ExpressionBreakpoint; +import tools.debugger.asyncstacktraces.ShadowStackEntry; +import tools.debugger.breakpoints.Breakpoints; import tools.debugger.entities.ActivityType; import tools.debugger.entities.BreakpointType; import tools.debugger.nodes.AbstractBreakpointNode; -import tools.debugger.session.Breakpoints; import tools.replay.TraceRecord; import tools.replay.nodes.RecordEventNodes.RecordOneEvent; @@ -155,14 +159,27 @@ public final SpawnPrim initialize(final VM vm) { } @Specialization(guards = "clazz == TaskClass") - @TruffleBoundary - public final SomForkJoinTask spawnTask(final SClass clazz, final SBlock block) { - SomForkJoinTask task = createTask(new Object[] {block}, + public final SomForkJoinTask spawnTask(VirtualFrame frame, final SClass clazz, + final SBlock block) { + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + ShadowStackEntry.EntryForTaskSpawn entry = ShadowStackEntry.createAtTaskSpawn(SArguments.getShadowStackEntry(frame),this); + arguments = new Object[] {block, entry }; + } else { + arguments = new Object[] {block}; + } + + SomForkJoinTask task = createTask(arguments, onExec.executeShouldHalt(), block, sourceSection, traceProcCreation, vm); - forkJoinPool.execute(task); + fork(task); return task; } + @TruffleBoundary + private void fork(final SomForkJoinTask task) { + forkJoinPool.execute(task); + } + @Specialization(guards = "clazz == ThreadClass") @TruffleBoundary public final SomThreadTask spawnThread(final SClass clazz, final SBlock block) { @@ -176,7 +193,7 @@ public final SomThreadTask spawnThread(final SClass clazz, final SBlock block) { public final Object spawnProcess(final VirtualFrame frame, final SImmutableObject procMod, final SClass procCls, @Cached("createIsValue()") final IsValue isVal) { if (!isVal.executeBoolean(frame, procCls)) { - notAValue.signal(procCls); + notAValue.signal(frame, procCls); } spawnProcess(procCls, traceProcCreation); @@ -268,10 +285,14 @@ public final Object spawnProcess(final VirtualFrame frame, final SImmutableObjec final SClass procCls, final SArray arg, final Object[] argArr, @Cached("createIsValue()") final IsValue isVal) { if (!isVal.executeBoolean(frame, procCls)) { - notAValue.signal(procCls); + notAValue.signal(frame, procCls); } - - spawnProcess(procCls, argArr, traceProcCreation); + Object[] arguments = argArr; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = Arrays.copyOf(argArr, argArr.length + 1); + arguments[argArr.length] = maybeEntry; + } + spawnProcess(procCls, arguments, traceProcCreation); return Nil.nilObject; } diff --git a/src/som/primitives/BlockPrims.java b/src/som/primitives/BlockPrims.java index 5818f25ee9..ae4f793a25 100644 --- a/src/som/primitives/BlockPrims.java +++ b/src/som/primitives/BlockPrims.java @@ -10,6 +10,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; @@ -31,6 +32,7 @@ import som.vmobjects.SArray; import som.vmobjects.SBlock; import som.vmobjects.SInvokable; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; import tools.dym.Tags.OpClosureApplication; import tools.dym.profiles.DispatchProfile; @@ -73,6 +75,8 @@ public abstract static class ValueNonePrim extends UnaryExpressionNode protected final HashMap targets = VmSettings.DYNAMIC_METRICS ? new HashMap<>() : null; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + @Override public ExpressionNode initialize(final SourceSection sourceSection, final boolean eagerlyWrapped) { @@ -98,17 +102,20 @@ public final boolean doBoolean(final boolean receiver) { @Specialization( guards = {"cached == receiver.getMethod()", "cached.getNumberOfArguments() == 1"}, limit = "CHAIN_LENGTH") - public final Object doCachedBlock(final SBlock receiver, + public final Object doCachedBlock(final VirtualFrame frame, final SBlock receiver, @Cached("createDirectCallNode(receiver, getThis())") final DirectCallNode call, @Cached("receiver.getMethod()") final SInvokable cached) { - return call.call(new Object[] {receiver}); + return call.call(SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, receiver)); } @Specialization(replaces = "doCachedBlock") - public final Object doGeneric(final SBlock receiver, + public final Object doGeneric(final VirtualFrame frame, final SBlock receiver, @Cached("create()") final IndirectCallNode call) { - checkArguments(receiver, 1, argumentError); - return receiver.getMethod().invoke(call, new Object[] {receiver}); + checkArguments(frame, receiver, 1, argumentError); + return receiver.getMethod().invoke(call, + SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, receiver)); } @Override @@ -125,11 +132,12 @@ public final void record(final Invokable ivkbl, } } - private static void checkArguments(final SBlock receiver, final int expectedNumArgs, + private static void checkArguments(final VirtualFrame frame, final SBlock receiver, + final int expectedNumArgs, final ExceptionSignalingNode argumentError) { int numArgs = receiver.getMethod().getNumberOfArguments(); if (numArgs != expectedNumArgs) { - argumentError.signal(errorMsg(expectedNumArgs, numArgs)); + argumentError.signal(frame, errorMsg(expectedNumArgs, numArgs)); } } @@ -147,6 +155,8 @@ public abstract static class ValueOnePrim extends BinaryExpressionNode implements DispatchProfile, ValuePrimNode { protected @Child ExceptionSignalingNode argumentError; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = + ShadowStackEntryLoad.create(); protected final HashMap targets = VmSettings.DYNAMIC_METRICS ? new HashMap<>() : null; @@ -171,17 +181,22 @@ protected boolean hasTagIgnoringEagerness(final Class tag) { @Specialization( guards = {"cached == receiver.getMethod()", "cached.getNumberOfArguments() == 2"}, limit = "CHAIN_LENGTH") - public final Object doCachedBlock(final SBlock receiver, final Object arg, + public final Object doCachedBlock(final VirtualFrame frame, final SBlock receiver, + final Object arg, @Cached("createDirectCallNode(receiver, getThis())") final DirectCallNode call, @Cached("receiver.getMethod()") final SInvokable cached) { - return call.call(new Object[] {receiver, arg}); + return call.call(SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, receiver, arg)); } @Specialization(replaces = "doCachedBlock") - public final Object doGeneric(final SBlock receiver, final Object arg, + public final Object doGeneric(final VirtualFrame frame, final SBlock receiver, + final Object arg, @Cached("create()") final IndirectCallNode call) { - checkArguments(receiver, 2, argumentError); - return receiver.getMethod().invoke(call, new Object[] {receiver, arg}); + checkArguments(frame, receiver, 2, argumentError); + return receiver.getMethod().invoke(call, + SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, receiver, arg)); } @Override @@ -206,6 +221,8 @@ public abstract static class ValueTwoPrim extends TernaryExpressionNode implements DispatchProfile, ValuePrimNode { protected @Child ExceptionSignalingNode argumentError; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = + ShadowStackEntryLoad.create(); protected final HashMap targets = VmSettings.DYNAMIC_METRICS ? new HashMap<>() : null; @@ -230,18 +247,23 @@ protected boolean hasTagIgnoringEagerness(final Class tag) { @Specialization( guards = {"cached == receiver.getMethod()", "cached.getNumberOfArguments() == 3"}, limit = "CHAIN_LENGTH") - public final Object doCachedBlock(final SBlock receiver, final Object arg1, + public final Object doCachedBlock(final VirtualFrame frame, final SBlock receiver, + final Object arg1, final Object arg2, @Cached("createDirectCallNode(receiver, getThis())") final DirectCallNode call, @Cached("receiver.getMethod()") final SInvokable cached) { - return call.call(new Object[] {receiver, arg1, arg2}); + return call.call(SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, receiver, arg1, arg2)); } @Specialization(replaces = "doCachedBlock") - public final Object doGeneric(final SBlock receiver, final Object arg1, final Object arg2, + public final Object doGeneric(final VirtualFrame frame, final SBlock receiver, + final Object arg1, final Object arg2, @Cached("create()") final IndirectCallNode call) { - checkArguments(receiver, 3, argumentError); - return receiver.getMethod().invoke(call, new Object[] {receiver, arg1, arg2}); + checkArguments(frame, receiver, 3, argumentError); + return receiver.getMethod().invoke(call, + SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, receiver, arg1, arg2)); } @Override @@ -266,9 +288,13 @@ public final void record(final Invokable ivkbl, public abstract static class ValueArgsPrim extends BinaryExpressionNode implements DispatchProfile, ValuePrimNode { - protected @Child SizeAndLengthPrim size = SizeAndLengthPrimFactory.create(null); - protected @Child AtPrim at = AtPrimFactory.create(null, null); + protected @Child SizeAndLengthPrim size = + SizeAndLengthPrimFactory.create(null); + protected @Child AtPrim at = + AtPrimFactory.create(null, null); protected @Child ExceptionSignalingNode argumentError; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = + ShadowStackEntryLoad.create(); protected final HashMap targets = VmSettings.DYNAMIC_METRICS ? new HashMap<>() : null; @@ -298,19 +324,23 @@ protected long getNumArgs(final SArray args) { guards = {"cached == receiver.getMethod()", "numArgs == cached.getNumberOfArguments()"}, limit = "CHAIN_LENGTH") - public final Object doCachedBlock(final SBlock receiver, final SArray args, + public final Object doCachedBlock(final VirtualFrame frame, final SBlock receiver, + final SArray args, @Cached("getNumArgs(args)") final long numArgs, @Cached("createDirectCallNode(receiver, getThis())") final DirectCallNode call, @Cached("receiver.getMethod()") final SInvokable cached) { - return call.call(SArguments.getPlainArgumentsWithReceiver(receiver, args, size, at)); + return call.call(SArguments.getPlainArgumentsWithReceiver(receiver, args, size, at, this, + shadowStackEntryLoad, frame)); } @Specialization(replaces = "doCachedBlock") - public final Object doGeneric(final SBlock receiver, final SArray args, + public final Object doGeneric(final VirtualFrame frame, final SBlock receiver, + final SArray args, @Cached("create()") final IndirectCallNode call) { - checkArguments(receiver, (int) getNumArgs(args), argumentError); + checkArguments(frame, receiver, (int) getNumArgs(args), argumentError); return receiver.getMethod().invoke( - call, SArguments.getPlainArgumentsWithReceiver(receiver, args, size, at)); + call, SArguments.getPlainArgumentsWithReceiver(receiver, args, size, at, this, + shadowStackEntryLoad, frame)); } @Override diff --git a/src/som/primitives/EqualsPrim.java b/src/som/primitives/EqualsPrim.java index dc7bb88410..b652217508 100644 --- a/src/som/primitives/EqualsPrim.java +++ b/src/som/primitives/EqualsPrim.java @@ -10,6 +10,7 @@ import bd.primitives.Primitive; import som.interpreter.actors.SFarReference; import som.vm.constants.Nil; +import som.vmobjects.SArray; import som.vmobjects.SObject.SImmutableObject; import som.vmobjects.SObjectWithClass; import som.vmobjects.SObjectWithClass.SObjectWithoutFields; @@ -73,6 +74,21 @@ public final boolean doValues(final SImmutableObject left, final SImmutableObjec return left == right; } + @Specialization(guards = {"left.isValue()", "right.isValue()"}) + public final boolean doValueArray(final SArray.SImmutableArray left, final SArray.SImmutableArray right) { + Object[] leftStorage = left.getObjectStorage(); + Object[] rightStorage = right.getObjectStorage(); + if (leftStorage.length != rightStorage.length){ + return false; + } + for(int i = 0;i < leftStorage.length ; i++){ + if(!leftStorage[i].equals(rightStorage[i])){ + return false; + } + } + return true; + } + @Specialization public final boolean doLong(final long left, final double right) { return left == right; diff --git a/src/som/primitives/ExceptionsPrims.java b/src/som/primitives/ExceptionsPrims.java index 1552722742..1f09ffba8a 100644 --- a/src/som/primitives/ExceptionsPrims.java +++ b/src/som/primitives/ExceptionsPrims.java @@ -4,10 +4,12 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import bd.primitives.Primitive; +import som.interpreter.SArguments; import som.interpreter.SomException; import som.interpreter.nodes.dispatch.BlockDispatchNode; import som.interpreter.nodes.dispatch.BlockDispatchNodeGen; @@ -19,6 +21,7 @@ import som.vmobjects.SBlock; import som.vmobjects.SClass; import som.vmobjects.SInvokable; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; public abstract class ExceptionsPrims { @@ -29,6 +32,12 @@ public abstract static class ExceptionDoOnPrim extends TernaryExpressionNode { protected static final int INLINE_CACHE_SIZE = VmSettings.DYNAMIC_METRICS ? 100 : 6; + @Child protected ShadowStackEntryLoad shadowStackEntryLoadBody = + ShadowStackEntryLoad.create(); + + @Child protected ShadowStackEntryLoad shadowStackEntryLoadException = + ShadowStackEntryLoad.create(); + protected static final IndirectCallNode indirect = Truffle.getRuntime().createIndirectCallNode(); @@ -44,17 +53,34 @@ public static final boolean sameBlock(final SBlock block, final SInvokable metho @Specialization(limit = "INLINE_CACHE_SIZE", guards = {"sameBlock(body, cachedBody)", "sameBlock(exceptionHandler, cachedExceptionMethod)"}) - public final Object doException(final SBlock body, + public final Object doException(final VirtualFrame frame, final SBlock body, final SClass exceptionClass, final SBlock exceptionHandler, @Cached("body.getMethod()") final SInvokable cachedBody, @Cached("createCallNode(body)") final DirectCallNode bodyCall, @Cached("exceptionHandler.getMethod()") final SInvokable cachedExceptionMethod, @Cached("createCallNode(exceptionHandler)") final DirectCallNode exceptionCall) { try { - return bodyCall.call(new Object[] {body}); + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {body, null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoadBody, frame, + false); + } else { + args = new Object[] {body}; + } + return bodyCall.call(args); } catch (SomException e) { if (e.getSomObject().getSOMClass().isKindOf(exceptionClass)) { - return exceptionCall.call(new Object[] {exceptionHandler, e.getSomObject()}); + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {exceptionHandler, e.getSomObject(), null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoadException, + frame, + false); + } else { + args = new Object[] {exceptionHandler, e.getSomObject()}; + } + return exceptionCall.call(args); } else { throw e; } @@ -62,14 +88,30 @@ public final Object doException(final SBlock body, } @Specialization(replaces = "doException") - public final Object doExceptionUncached(final SBlock body, + public final Object doExceptionUncached(final VirtualFrame frame, final SBlock body, final SClass exceptionClass, final SBlock exceptionHandler) { try { - return body.getMethod().invoke(indirect, new Object[] {body}); + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {body, null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoadBody, frame, + false); + } else { + args = new Object[] {body}; + } + return body.getMethod().invoke(indirect, args); } catch (SomException e) { if (e.getSomObject().getSOMClass().isKindOf(exceptionClass)) { - return exceptionHandler.getMethod().invoke(indirect, - new Object[] {exceptionHandler, e.getSomObject()}); + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {exceptionHandler, e.getSomObject(), null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoadException, + frame, + false); + } else { + args = new Object[] {exceptionHandler, e.getSomObject()}; + } + return exceptionHandler.getMethod().invoke(indirect, args); } else { throw e; } @@ -94,12 +136,34 @@ public abstract static class EnsurePrim extends BinaryComplexOperation { @Child protected BlockDispatchNode dispatchBody = BlockDispatchNodeGen.create(); @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); + @Child protected ShadowStackEntryLoad shadowStackEntryLoadBody = + ShadowStackEntryLoad.create(); + @Child protected ShadowStackEntryLoad shadowStackEntryLoadHandler = + ShadowStackEntryLoad.create(); + @Specialization - public final Object doException(final SBlock body, final SBlock ensureHandler) { - try { - return dispatchBody.executeDispatch(new Object[] {body}); - } finally { - dispatchHandler.executeDispatch(new Object[] {ensureHandler}); + public final Object doException(final VirtualFrame frame, final SBlock body, + final SBlock ensureHandler) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + // losing SSEntry info here again + try { + Object[] args = new Object[] {body, null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoadBody, frame, + false); + return dispatchBody.executeDispatch(frame, args); + } finally { + Object[] args = new Object[] {ensureHandler, null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoadHandler, + frame, + false); + dispatchHandler.executeDispatch(frame, args); + } + } else { + try { + return dispatchBody.executeDispatch(frame, new Object[] {body}); + } finally { + dispatchHandler.executeDispatch(frame, new Object[] {ensureHandler}); + } } } } diff --git a/src/som/primitives/FilePrims.java b/src/som/primitives/FilePrims.java index c8dc07af08..ae18efb49a 100644 --- a/src/som/primitives/FilePrims.java +++ b/src/som/primitives/FilePrims.java @@ -7,6 +7,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.source.SourceSection; @@ -68,8 +69,8 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - public final Object closeFile(final SFileDescriptor file) { - file.closeFile(ioException); + public final Object closeFile(final VirtualFrame frame, final SFileDescriptor file) { + file.closeFile(frame, ioException); return file; } } @@ -122,18 +123,20 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - public final Object setModeSymbol(final SFileDescriptor file, final SSymbol mode) { + public final Object setModeSymbol(final VirtualFrame frame, final SFileDescriptor file, + final SSymbol mode) { try { file.setMode(mode); } catch (IllegalArgumentException e) { - argumentError.signal(mode.getString()); + argumentError.signal(frame, mode.getString()); } return file; } @Fallback - public final Object setWithUnsupportedValue(final Object file, final Object mode) { - argumentError.signal(errorMsg(mode)); + public final Object setWithUnsupportedValue(final VirtualFrame frame, final Object file, + final Object mode) { + argumentError.signal(frame, errorMsg(mode)); return file; } @@ -159,8 +162,8 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - public final long getFileSize(final SFileDescriptor file) { - return file.getFileSize(ioException); + public final long getFileSize(final VirtualFrame frame, final SFileDescriptor file) { + return file.getFileSize(frame, ioException); } } @@ -179,8 +182,9 @@ public abstract static class FileOpenPrim extends BinaryExpressionNode { @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); @Specialization - public final Object fileOpen(final SFileDescriptor file, final SBlock handler) { - return file.openFile(handler, dispatchHandler); + public final Object fileOpen(final VirtualFrame frame, final SFileDescriptor file, + final SBlock handler) { + return file.openFile(frame, handler, dispatchHandler); } } @@ -192,9 +196,9 @@ public abstract static class ReadFilePrim extends TernaryExpressionNode { @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); @Specialization - public final long read(final SFileDescriptor file, final long offset, + public final long read(VirtualFrame frame, final SFileDescriptor file, final long offset, final SBlock fail) { - return file.read(offset, fail, dispatchHandler, errorCases); + return file.read(frame, offset, fail, dispatchHandler, errorCases); } } @@ -216,9 +220,10 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - public final Object write(final SFileDescriptor file, final long nBytes, + public final Object write(final VirtualFrame frame, final SFileDescriptor file, + final long nBytes, final long offset, final SBlock fail) { - file.write((int) nBytes, offset, fail, dispatchHandler, ioException, errorCases); + file.write(frame, (int) nBytes, offset, fail, dispatchHandler, ioException, errorCases); return file; } } diff --git a/src/som/primitives/MirrorPrims.java b/src/som/primitives/MirrorPrims.java index 6fcdda9a82..8e80783bc0 100644 --- a/src/som/primitives/MirrorPrims.java +++ b/src/som/primitives/MirrorPrims.java @@ -14,6 +14,7 @@ import bd.primitives.Primitive; import som.VM; import som.compiler.MixinDefinition; +import som.interpreter.SArguments; import som.interpreter.Types; import som.interpreter.nodes.dispatch.Dispatchable; import som.interpreter.nodes.nary.BinaryComplexOperation; diff --git a/src/som/primitives/ObjectPrims.java b/src/som/primitives/ObjectPrims.java index 52765976de..bd0ea02ea1 100644 --- a/src/som/primitives/ObjectPrims.java +++ b/src/som/primitives/ObjectPrims.java @@ -1,7 +1,10 @@ package som.primitives; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.debug.DebuggerTags.AlwaysHalt; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -11,20 +14,24 @@ import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.ProbeNode; import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.api.source.SourceSection; import bd.primitives.Primitive; import bd.tools.nodes.Operation; import som.Output; import som.VM; +import som.interpreter.SArguments; import som.interpreter.Types; import som.interpreter.actors.SFarReference; import som.interpreter.actors.SPromise; import som.interpreter.actors.SPromise.SResolver; +import som.interpreter.nodes.dispatch.CachedDispatchNode; import som.interpreter.nodes.nary.UnaryBasicOperation; import som.interpreter.nodes.nary.UnaryExpressionNode; import som.interpreter.processes.SChannel.SChannelInput; import som.interpreter.processes.SChannel.SChannelOutput; import som.primitives.ObjectPrimsFactory.IsValueFactory; +import som.vm.constants.Classes; import som.vm.constants.Nil; import som.vmobjects.SAbstractObject; import som.vmobjects.SArray.SImmutableArray; @@ -35,11 +42,91 @@ import som.vmobjects.SObject.SMutableObject; import som.vmobjects.SObjectWithClass.SObjectWithoutFields; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntry; +import tools.debugger.asyncstacktraces.StackIterator; +import tools.debugger.frontend.ApplicationThreadStack; import tools.dym.Tags.OpComparison; public final class ObjectPrims { + @GenerateNodeFactory + @Primitive(primitive = "asyncTrace:") + public abstract static class AsyncTracePrim extends UnaryExpressionNode { + @Specialization + public final Object doSAbstractObject(VirtualFrame frame, final Object receiver) { + CompilerDirectives.transferToInterpreter(); + + Output.errorPrintln("ASYNC STACK TRACE"); + StackIterator.ShadowStackIterator iterator = + new StackIterator.ShadowStackIterator.HaltShadowStackIterator(this.sourceSection); + List stack = new ArrayList<>(); + while (iterator.hasNext()) { + ApplicationThreadStack.StackFrame sf = iterator.next(); + if (sf != null) { + SourceSection section = sf.section; + String isFromMCOpt = (sf.fromMethodCache) ? ", MC" : ""; + if (sf instanceof ApplicationThreadStack.ParallelStack){ + for (List stackList : ((ApplicationThreadStack.ParallelStack) sf).parallelStacks){ + stack.add("PromiseGroup Parallel Stack"); + for (ApplicationThreadStack.StackFrame f : stackList){ + stack.add(" => ," + f.name + ", " + f.section.getSource().getName() + ", " + f.section.getStartLine()); + } + } + } else { + stack.add( + sf.name + ", " + section.getSource().getName() + ", " + section.getStartLine() + isFromMCOpt); + } + } + } + return new SImmutableArray(stack.toArray(), Classes.arrayClass); + } + + @Override + protected boolean hasTagIgnoringEagerness(final Class tag) { + if (tag == AlwaysHalt.class) { + return true; + } + return super.hasTagIgnoringEagerness(tag); + } + } + + @GenerateNodeFactory + @Primitive(primitive = "resetAsyncTrace:") + public abstract static class ResetAsyncTracePrim extends UnaryExpressionNode { + @Specialization + public final Object doSAbstractObject(VirtualFrame frame, final Object receiver) { + Output.errorPrintln("RESETTING ASYNC STACK TRACE"); + ShadowStackEntry currentEntry = SArguments.getShadowStackEntry(frame); + boolean keepLooping = true; + String methodName; + while (keepLooping && currentEntry != null) { + if (currentEntry instanceof ShadowStackEntry.EntryForPromiseResolution) { + methodName = + ((ShadowStackEntry.EntryForPromiseResolution) currentEntry).resolutionLocation.toString(); + } else { + methodName = + ((CachedDispatchNode) currentEntry.getExpression()).getCachedMethod().getName(); + } + if (methodName.equals("ON_WHEN_RESOLVED_BLOCK")) { + keepLooping = false; + } else { + currentEntry = currentEntry.getPreviousShadowStackEntry(); + } + } + currentEntry.setPreviousShadowStackEntry(ShadowStackEntry.createTop(this)); + return "RESET"; + } + + @Override + protected boolean hasTagIgnoringEagerness(final Class tag) { + if (tag == AlwaysHalt.class) { + return true; + } + return super.hasTagIgnoringEagerness(tag); + } + } + @GenerateNodeFactory @Primitive(primitive = "objClassName:") public abstract static class ObjectClassNamePrim extends UnaryExpressionNode { diff --git a/src/som/primitives/PathPrims.java b/src/som/primitives/PathPrims.java index 58ff09857d..3f55c825e8 100644 --- a/src/som/primitives/PathPrims.java +++ b/src/som/primitives/PathPrims.java @@ -17,6 +17,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import bd.primitives.Primitive; @@ -39,6 +40,11 @@ public final class PathPrims { @CompilationFinal private static SImmutableObject fileObject; + @TruffleBoundary + private static String getMessage(final Exception e) { + return e.getMessage(); + } + public static final class FileModule implements Supplier { @Override public SObject get() { @@ -87,11 +93,12 @@ public abstract static class FileCopyPrim extends TernaryExpressionNode { @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); @Specialization - public final Object copyAs(final String source, final String dest, final SBlock fail) { + public final Object copyAs(final VirtualFrame frame, final String source, + final String dest, final SBlock fail) { try { copy(source, dest); } catch (IOException e) { - dispatchHandler.executeDispatch(new Object[] {fail, PathPrims.toString(e)}); + dispatchHandler.executeDispatch(frame, new Object[] {fail, PathPrims.toString(e)}); } return Nil.nilObject; } @@ -108,11 +115,12 @@ public abstract static class CreateDirectoryPrim extends BinaryExpressionNode { @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); @Specialization - public final Object createDirectory(final String dir, final SBlock fail) { + public final Object createDirectory(final VirtualFrame frame, final String dir, + final SBlock fail) { try { createDirectory(dir); } catch (IOException e) { - dispatchHandler.executeDispatch(new Object[] {fail, PathPrims.toString(e)}); + dispatchHandler.executeDispatch(frame, new Object[] {fail, PathPrims.toString(e)}); } return Nil.nilObject; } @@ -129,11 +137,12 @@ public abstract static class DeleteDirectoryPrim extends BinaryExpressionNode { @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); @Specialization - public final Object delteDirectory(final String dir, final SBlock fail) { + public final Object delteDirectory(final VirtualFrame frame, final String dir, + final SBlock fail) { try { delete(dir); } catch (IOException e) { - dispatchHandler.executeDispatch(new Object[] {fail, PathPrims.toString(e)}); + dispatchHandler.executeDispatch(frame, new Object[] {fail, PathPrims.toString(e)}); } return Nil.nilObject; } @@ -192,15 +201,14 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - @TruffleBoundary - public final Object lastModified(final String dir) { + public final Object lastModified(final VirtualFrame frame, final String dir) { try { return lastModifiedTime(dir); } catch (FileNotFoundException e) { - fileNotFound.signal(dir, e.getMessage()); + fileNotFound.signal(frame, dir, getMessage(e)); return Nil.nilObject; } catch (IOException e) { - ioException.signal(e.getMessage()); + ioException.signal(frame, getMessage(e)); return Nil.nilObject; } } @@ -217,11 +225,12 @@ public abstract static class FileMovePrim extends TernaryExpressionNode { @Child protected BlockDispatchNode dispatchHandler = BlockDispatchNodeGen.create(); @Specialization - public final Object moveAs(final String source, final String dest, final SBlock fail) { + public final Object moveAs(final VirtualFrame frame, final String source, + final String dest, final SBlock fail) { try { move(source, dest); } catch (IOException e) { - dispatchHandler.executeDispatch(new Object[] {fail, PathPrims.toString(e)}); + dispatchHandler.executeDispatch(frame, new Object[] {fail, PathPrims.toString(e)}); } return Nil.nilObject; } @@ -250,14 +259,13 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - @TruffleBoundary - public final long getSize(final String dir) { + public final long getSize(final VirtualFrame frame, final String dir) { try { return size(dir); } catch (NoSuchFileException e) { - fileNotFound.signal(dir, e.getMessage()); + fileNotFound.signal(frame, dir, getMessage(e)); } catch (IOException e) { - ioException.signal(e.getMessage()); + ioException.signal(frame, getMessage(e)); } return -1; } diff --git a/src/som/primitives/StringPrims.java b/src/som/primitives/StringPrims.java index 3900b93a65..b7c40cec8f 100644 --- a/src/som/primitives/StringPrims.java +++ b/src/som/primitives/StringPrims.java @@ -1,9 +1,11 @@ package som.primitives; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.source.SourceSection; @@ -182,14 +184,16 @@ public ExpressionNode initialize(final SourceSection sourceSection, } @Specialization - public final String doString(final SArray chars) { + public final String doString(final VirtualFrame frame, final SArray chars) { VM.thisMethodNeedsToBeOptimized( "Method not yet optimal for compilation, should speculate or use branch profile in the loop"); - return doStringWithBoundary(chars); + return doStringWithBoundary(frame, chars); } - @TruffleBoundary - private String doStringWithBoundary(final SArray chars) { + private String doStringWithBoundary(final VirtualFrame frame, final SArray chars) { + // Because of the frame, we can't have a boundary, so, just transferToInterpreter + CompilerDirectives.transferToInterpreter(); + Object[] storage = chars.getObjectStorage(); StringBuilder sb = new StringBuilder(storage.length); for (Object o : storage) { @@ -199,7 +203,7 @@ private String doStringWithBoundary(final SArray chars) { sb.append(((SSymbol) o).getString()); } else { // TODO: there should be a Smalltalk asString message here, I think - argumentError.signal(errorMsg(o)); + argumentError.signal(frame, errorMsg(o)); } } @@ -212,8 +216,8 @@ private static String errorMsg(final Object o) { } @Fallback - public final void doGeneric(final Object obj) { - argumentError.signal(obj); + public final void doGeneric(final VirtualFrame frame, final Object obj) { + argumentError.signal(frame, obj); } } @@ -268,8 +272,8 @@ public final String doUnicodeChar(final long val) { } @Fallback - public final void doGeneric(final Object val) { - argumentError.signal("The value " + val + " is not a valid Unicode code point."); + public final void doGeneric(final VirtualFrame frame, final Object val) { + argumentError.signal(frame, "The value " + val + " is not a valid Unicode code point."); } } diff --git a/src/som/primitives/SystemPrims.java b/src/som/primitives/SystemPrims.java index fb68789686..22b95f9c1c 100644 --- a/src/som/primitives/SystemPrims.java +++ b/src/som/primitives/SystemPrims.java @@ -7,18 +7,16 @@ import java.io.IOException; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.RootCallTarget; -import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.frame.FrameInstanceVisitor; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; @@ -26,7 +24,6 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.CachedLibrary; -import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; import bd.primitives.Primitive; @@ -38,7 +35,6 @@ import som.compiler.MixinDefinition; import som.interop.ValueConversion.ToSomConversion; import som.interop.ValueConversionFactory.ToSomConversionNodeGen; -import som.interpreter.Invokable; import som.interpreter.Types; import som.interpreter.actors.Actor.ActorProcessingThread; import som.interpreter.actors.EventualMessage; @@ -63,6 +59,10 @@ import som.vmobjects.SSymbol; import tools.concurrency.TracingActors.TracingActor; import tools.concurrency.TracingBackend; +import tools.debugger.asyncstacktraces.ShadowStackEntry; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; +import tools.debugger.asyncstacktraces.StackIterator; +import tools.debugger.frontend.ApplicationThreadStack.StackFrame; import tools.dym.Tags.BasicPrimitiveOperation; import tools.replay.actors.UniformExecutionTrace; import tools.replay.nodes.TraceContextNode; @@ -95,7 +95,17 @@ public abstract static class TraceStatisticsPrim extends UnarySystemOperation { @Specialization @TruffleBoundary public final Object doSObject(final Object module) { - long[] stats = TracingBackend.getStatistics(); + long[] tracingStats = TracingBackend.getStatistics(); + long[] stats = Arrays.copyOf(tracingStats, tracingStats.length + 5); + stats[tracingStats.length] = ShadowStackEntry.numberOfAllocations; + ShadowStackEntry.numberOfAllocations = 0; + stats[tracingStats.length + 1] = 0; + stats[tracingStats.length + 2] = ShadowStackEntryLoad.cacheHit; + ShadowStackEntryLoad.cacheHit = 0; + stats[tracingStats.length + 3] = ShadowStackEntryLoad.megaMiss; + ShadowStackEntryLoad.megaMiss = 0; + stats[tracingStats.length + 4] = ShadowStackEntryLoad.megaCacheHit; + ShadowStackEntryLoad.megaCacheHit = 0; return new SImmutableArray(stats, Classes.valueArrayClass); } } @@ -131,27 +141,49 @@ public final Object doSObject(final Object module) { } } - public static Object loadModule(final VM vm, final String path, + @TruffleBoundary + private static String concat(final String a, final Exception e) { + return a.concat(e.getMessage()); + } + + @TruffleBoundary + private static String concat(final String a, final String b) { + return a.concat(b); + } + + @TruffleBoundary + private static String getMessage(final IOException e) { + return e.getMessage(); + } + + public static Object loadModule(final VirtualFrame frame, final VM vm, final String path, final ExceptionSignalingNode ioException) { // TODO: a single node for the different exceptions? try { - if (path.endsWith(EXTENSION_EXT)) { - return vm.loadExtensionModule(path); - } else { - MixinDefinition module = vm.loadModule(path); - return module.instantiateModuleClass(); - } + return loadModule(vm, path); } catch (FileNotFoundException e) { - ioException.signal(path, "Could not find module file. " + e.getMessage()); + ioException.signal(frame, path, + concat("Could not find module file. ", e)); } catch (NotAFileException e) { - ioException.signal(path, "Path does not seem to be a file. " + e.getMessage()); + ioException.signal(frame, path, + concat("Path does not seem to be a file. ", e)); } catch (IOException e) { - ioException.signal(e.getMessage()); + ioException.signal(frame, getMessage(e)); } assert false : "This should never be reached, because exceptions do not return"; return Nil.nilObject; } + @TruffleBoundary + private static Object loadModule(final VM vm, final String path) throws IOException { + if (path.endsWith(EXTENSION_EXT)) { + return vm.loadExtensionModule(path); + } else { + MixinDefinition module = vm.loadModule(path); + return module.instantiateModuleClass(); + } + } + @GenerateNodeFactory @Primitive(primitive = "load:") public abstract static class LoadPrim extends UnarySystemOperation { @@ -166,9 +198,8 @@ public UnarySystemOperation initialize(final VM vm) { } @Specialization - @TruffleBoundary - public final Object doSObject(final String moduleName) { - return loadModule(vm, moduleName, ioException); + public final Object doSObject(final VirtualFrame frame, final String moduleName) { + return loadModule(frame, vm, moduleName, ioException); } } @@ -186,13 +217,20 @@ public BinarySystemOperation initialize(final VM vm) { } @Specialization + public final Object load(final VirtualFrame frame, final String filename, + final SObjectWithClass moduleObj) { + String pathWithBasePath = getPathWithBase(filename, moduleObj); + return loadModule(frame, vm, pathWithBasePath, ioException); + } + @TruffleBoundary - public final Object load(final String filename, final SObjectWithClass moduleObj) { + private String getPathWithBase(final String filename, final SObjectWithClass moduleObj) { String path = moduleObj.getSOMClass().getMixinDefinition().getSourceSection().getSource() .getPath(); File file = new File(URI.create(path).getPath()); - return loadModule(vm, file.getParent() + File.separator + filename, ioException); + String pathWithBasePath = file.getParent() + File.separator + filename; + return pathWithBasePath; } } @@ -291,57 +329,57 @@ public final Object doSObject(final Object receiver) { public static void printStackTrace(final int skipDnuFrames, final SourceSection topNode) { ArrayList method = new ArrayList(); ArrayList location = new ArrayList(); - int[] maxLengthMethod = {0}; - boolean[] first = {true}; - Output.println("Stack Trace"); - - Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { - @Override - public Object visitFrame(final FrameInstance frameInstance) { - RootCallTarget ct = (RootCallTarget) frameInstance.getCallTarget(); - - // TODO: do we need to handle other kinds of root nodes? - if (!(ct.getRootNode() instanceof Invokable)) { - return null; - } - - Invokable m = (Invokable) ct.getRootNode(); - - String id = m.getName(); - method.add(id); - maxLengthMethod[0] = Math.max(maxLengthMethod[0], id.length()); - Node callNode = frameInstance.getCallNode(); - if (callNode != null || first[0]) { - SourceSection nodeSS; - if (first[0]) { - first[0] = false; - nodeSS = topNode; - } else { - nodeSS = callNode.getEncapsulatingSourceSection(); - } - if (nodeSS != null) { - location.add(nodeSS.getSource().getName() - + SourceCoordinate.getLocationQualifier(nodeSS)); - } else { - location.add(""); - } - } else { - location.add(""); - } - - return null; + int maxLengthMethod = 0; + + //StackIterator stack = StackIterator.createHaltIterator(topNode); + StackIterator stack = new StackIterator.HaltIterator(topNode); + boolean asyncTrace = false; + + if (stack instanceof StackIterator.ShadowStackIterator.HaltShadowStackIterator) { + Output.println("Async Stack Trace"); + asyncTrace = true; + } else { + Output.println("Stack Trace"); + } + + while (stack.hasNext()) { + StackFrame frame = stack.next(); + if (frame != null) { + method.add(frame.name); + maxLengthMethod = Math.max(maxLengthMethod, frame.name.length()); + // note: `callNode.getEncapsulatingSourceSection();` is better than frame.section + // because with this one we can get the source section while the other option returns + // null + addSourceSection(frame.section, location); } - }); + } + + // for async traces hide only 1 frame: Thing>>#error:, because we want to show the last + // frame although is not async + Output.print(stringStackTraceFrom(method, location, maxLengthMethod, + asyncTrace == false ? skipDnuFrames : 1)); + } + private static String stringStackTraceFrom(final ArrayList method, + final ArrayList location, final int maxLengthMethod, final int skipDnuFrames) { StringBuilder sb = new StringBuilder(); for (int i = method.size() - 1; i >= skipDnuFrames; i--) { - sb.append(String.format("\t%1$-" + (maxLengthMethod[0] + 4) + "s", + sb.append(String.format("\t%1$-" + (maxLengthMethod + 4) + "s", method.get(i))); sb.append(location.get(i)); sb.append('\n'); } + return sb.toString(); + } - Output.print(sb.toString()); + private static void addSourceSection(final SourceSection section, + final ArrayList location) { + if (section != null) { + location.add(section.getSource().getName() + + SourceCoordinate.getLocationQualifier(section)); + } else { + location.add(""); + } } } diff --git a/src/som/primitives/TimerPrim.java b/src/som/primitives/TimerPrim.java index 4165dce844..877eabf1af 100644 --- a/src/som/primitives/TimerPrim.java +++ b/src/som/primitives/TimerPrim.java @@ -18,6 +18,7 @@ import bd.primitives.Primitive; import som.VM; import som.compiler.AccessModifier; +import som.interpreter.SArguments; import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage.DirectMessage; import som.interpreter.actors.EventualSendNode; @@ -31,8 +32,8 @@ import som.vmobjects.SSymbol; import tools.concurrency.TracingActors.ReplayActor; import tools.replay.TraceParser; -import tools.replay.actors.UniformExecutionTrace; import tools.replay.actors.ExternalEventualMessage.ExternalDirectMessage; +import tools.replay.actors.UniformExecutionTrace; import tools.replay.nodes.TraceContextNode; import tools.replay.nodes.TraceContextNodeGen; @@ -101,8 +102,10 @@ protected final Object perform(final Object target, final Actor targetActor, timer.schedule(new TimerTask() { @Override public void run() { + Object[] args = SArguments.convertToArgumentArray(new Object[] {target}); + ExternalDirectMessage msg = new ExternalDirectMessage(targetActor, - VALUE_SELECTOR, new Object[] {target}, timerActor, null, valueCallTarget, + VALUE_SELECTOR, args, timerActor, null, valueCallTarget, (short) 0, id); targetActor.send(msg, actorPool); } diff --git a/src/som/primitives/actors/CreateActorPrim.java b/src/som/primitives/actors/CreateActorPrim.java index 9edad9648e..fb0a4c7c1b 100644 --- a/src/som/primitives/actors/CreateActorPrim.java +++ b/src/som/primitives/actors/CreateActorPrim.java @@ -57,13 +57,17 @@ public final SFarReference createActor(final VirtualFrame frame, final Object re final SClass actorClass = (SClass) argument; KomposTrace.activityCreation(ActivityType.ACTOR, actor.getId(), actorClass.getName(), sourceSection); + // to keep all the created actors, this information is needed for example when pausing a + // running actor without specifying a breakpoint + TracingActor.saveActor(actor); } return ref; } @Fallback - public final Object throwNotAValueException(final Object receiver, final Object argument) { - return notAValue.signal(argument); + public final Object throwNotAValueException(final VirtualFrame frame, final Object receiver, + final Object argument) { + return notAValue.signal(frame, argument); } @Override diff --git a/src/som/primitives/actors/PromisePrims.java b/src/som/primitives/actors/PromisePrims.java index 38c8b264df..2ba4a5ae8d 100644 --- a/src/som/primitives/actors/PromisePrims.java +++ b/src/som/primitives/actors/PromisePrims.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.DirectCallNode; @@ -17,6 +18,7 @@ import bd.tools.nodes.Operation; import som.VM; import som.compiler.AccessModifier; +import som.interpreter.SArguments; import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage; import som.interpreter.actors.EventualMessage.PromiseCallbackMessage; @@ -32,6 +34,8 @@ import som.interpreter.nodes.nary.UnaryExpressionNode.UnarySystemOperation; import som.vm.Symbols; import som.vm.VmSettings; +import som.vm.constants.Classes; +import som.vmobjects.SArray; import som.vmobjects.SBlock; import som.vmobjects.SInvokable; import som.vmobjects.SObject.SImmutableObject; @@ -42,10 +46,13 @@ import tools.concurrency.Tags.OnError; import tools.concurrency.Tags.WhenResolved; import tools.concurrency.Tags.WhenResolvedOnError; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; +import tools.debugger.breakpoints.Breakpoints; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.SendOp; import tools.debugger.nodes.AbstractBreakpointNode; -import tools.debugger.session.Breakpoints; + +import java.util.concurrent.ForkJoinPool; public final class PromisePrims { @@ -74,6 +81,8 @@ public abstract static class CreatePromisePairPrim extends UnarySystemOperation @Child protected AbstractBreakpointNode promiseResolverBreakpoint; @Child protected AbstractBreakpointNode promiseResolutionBreakpoint; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + protected static final DirectCallNode create() { Dispatchable disp = SPromise.pairClass.getSOMClass().lookupMessage( withAndFactory, AccessModifier.PUBLIC); @@ -91,7 +100,7 @@ public final CreatePromisePairPrim initialize(final VM vm) { } @Specialization - public final SImmutableObject createPromisePair(final Object nil, + public final SImmutableObject createPromisePair(final VirtualFrame frame, final Object nil, @Cached("create()") final DirectCallNode factory) { SPromise promise = SPromise.createPromise( @@ -100,8 +109,15 @@ public final SImmutableObject createPromisePair(final Object nil, promiseResolutionBreakpoint.executeShouldHalt(), sourceSection); SResolver resolver = SPromise.createResolver(promise); - return (SImmutableObject) factory.call( - new Object[] {SPromise.pairClass, promise, resolver}); + Object[] args; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + args = new Object[] {SPromise.pairClass, promise, resolver, null}; + SArguments.setShadowStackEntryWithCache(args, this, shadowStackEntryLoad, + frame, false); + } else { + args = new Object[] {SPromise.pairClass, promise, resolver}; + } + return (SImmutableObject) factory.call(args); } private static final SSymbol withAndFactory = Symbols.symbolFor("with:and:"); @@ -130,8 +146,7 @@ public int getNumArguments() { // does not require node creation? Might need a generic received node. @TruffleBoundary public static RootCallTarget createReceived(final SBlock callback) { - RootCallTarget target = callback.getMethod().getCallTarget(); - ReceivedCallback node = new ReceivedCallback(target); + ReceivedCallback node = new ReceivedCallback(callback.getMethod()); return node.getCallTarget(); } @@ -157,19 +172,22 @@ public final WhenResolvedPrim initialize(final VM vm) { } @Specialization(guards = "blockMethod == callback.getMethod()", limit = "10") - public final SPromise whenResolved(final SPromise promise, + public final SPromise whenResolved(final VirtualFrame frame, final SPromise promise, final SBlock callback, @Cached("callback.getMethod()") final SInvokable blockMethod, @Cached("createReceived(callback)") final RootCallTarget blockCallTarget) { - return registerWhenResolved(promise, callback, blockCallTarget, registerNode); + return registerWhenResolved(frame, promise, callback, blockCallTarget, registerNode); } @Specialization(replaces = "whenResolved") - public final SPromise whenResolvedUncached(final SPromise promise, final SBlock callback) { - return registerWhenResolved(promise, callback, createReceived(callback), registerNode); + public final SPromise whenResolvedUncached(final VirtualFrame frame, + final SPromise promise, final SBlock callback) { + return registerWhenResolved(frame, promise, callback, createReceived(callback), + registerNode); } - protected final SPromise registerWhenResolved(final SPromise rcvr, + protected final SPromise registerWhenResolved(final VirtualFrame frame, + final SPromise rcvr, final SBlock block, final RootCallTarget blockCallTarget, final RegisterWhenResolved registerNode) { assert block.getMethod().getNumberOfArguments() == 2; @@ -186,9 +204,12 @@ protected final SPromise registerWhenResolved(final SPromise rcvr, if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.PROMISE_MSG, pcm.getMessageId(), - rcvr.getPromiseId()); + rcvr.getPromiseId(), pcm.getSelector(), rcvr.getOwner().getId(), + pcm.getTargetSourceSection()); } - registerNode.register(rcvr, pcm, current); + registerNode.register(frame, rcvr, pcm, current); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + || pcm.getArgs()[pcm.getArgs().length - 1] != null; return promise; } @@ -224,19 +245,20 @@ public final OnErrorPrim initialize(final VM vm) { } @Specialization(guards = "blockMethod == callback.getMethod()", limit = "10") - public final SPromise onError(final SPromise promise, + public final SPromise onError(final VirtualFrame frame, final SPromise promise, final SBlock callback, @Cached("callback.getMethod()") final SInvokable blockMethod, @Cached("createReceived(callback)") final RootCallTarget blockCallTarget) { - return registerOnError(promise, callback, blockCallTarget, registerNode); + return registerOnError(frame, promise, callback, blockCallTarget, registerNode); } @Specialization(replaces = "onError") - public final SPromise whenResolvedUncached(final SPromise promise, final SBlock callback) { - return registerOnError(promise, callback, createReceived(callback), registerNode); + public final SPromise whenResolvedUncached(final VirtualFrame frame, + final SPromise promise, final SBlock callback) { + return registerOnError(frame, promise, callback, createReceived(callback), registerNode); } - protected final SPromise registerOnError(final SPromise rcvr, + protected final SPromise registerOnError(final VirtualFrame frame, final SPromise rcvr, final SBlock block, final RootCallTarget blockCallTarget, final RegisterOnError registerNode) { assert block.getMethod().getNumberOfArguments() == 2; @@ -253,9 +275,12 @@ protected final SPromise registerOnError(final SPromise rcvr, if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.PROMISE_MSG, msg.getMessageId(), - rcvr.getPromiseId()); + rcvr.getPromiseId(), msg.getSelector(), rcvr.getOwner().getId(), + msg.getTargetSourceSection()); } - registerNode.register(rcvr, msg, current); + registerNode.register(frame, rcvr, msg, current); + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + || msg.getArgs()[msg.getArgs().length - 1] != null; return promise; } @@ -293,24 +318,27 @@ public final WhenResolvedOnErrorPrim initialize(final VM vm) { @Specialization(guards = {"resolvedMethod == resolved.getMethod()", "errorMethod == error.getMethod()"}) - public final SPromise whenResolvedOnError(final SPromise promise, + public final SPromise whenResolvedOnError(final VirtualFrame frame, final SPromise promise, final SBlock resolved, final SBlock error, @Cached("resolved.getMethod()") final SInvokable resolvedMethod, @Cached("createReceived(resolved)") final RootCallTarget resolvedTarget, @Cached("error.getMethod()") final SInvokable errorMethod, @Cached("createReceived(error)") final RootCallTarget errorTarget) { - return registerWhenResolvedOrError(promise, resolved, error, resolvedTarget, + return registerWhenResolvedOrError(frame, promise, resolved, error, resolvedTarget, errorTarget, registerWhenResolved, registerOnError); } @Specialization(replaces = "whenResolvedOnError") - public final SPromise whenResolvedOnErrorUncached(final SPromise promise, + public final SPromise whenResolvedOnErrorUncached(final VirtualFrame frame, + final SPromise promise, final SBlock resolved, final SBlock error) { - return registerWhenResolvedOrError(promise, resolved, error, createReceived(resolved), + return registerWhenResolvedOrError(frame, promise, resolved, error, + createReceived(resolved), createReceived(error), registerWhenResolved, registerOnError); } - protected final SPromise registerWhenResolvedOrError(final SPromise rcvr, + protected final SPromise registerWhenResolvedOrError(final VirtualFrame frame, + final SPromise rcvr, final SBlock resolved, final SBlock error, final RootCallTarget resolverTarget, final RootCallTarget errorTarget, final RegisterWhenResolved registerWhenResolved, @@ -332,15 +360,21 @@ protected final SPromise registerWhenResolvedOrError(final SPromise rcvr, if (VmSettings.KOMPOS_TRACING) { KomposTrace.sendOperation(SendOp.PROMISE_MSG, onResolved.getMessageId(), - rcvr.getPromiseId()); + rcvr.getPromiseId(), onResolved.getSelector(), onResolved.getTarget().getId(), + onResolved.getTargetSourceSection()); KomposTrace.sendOperation(SendOp.PROMISE_MSG, onError.getMessageId(), - rcvr.getPromiseId()); + rcvr.getPromiseId(), onError.getSelector(), onError.getTarget().getId(), + onResolved.getTargetSourceSection()); } synchronized (rcvr) { - registerWhenResolved.register(rcvr, onResolved, current); - registerOnError.register(rcvr, onError, current); + registerWhenResolved.register(frame, rcvr, onResolved, current); + registerOnError.register(frame, rcvr, onError, current); } + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + || onResolved.getArgs()[onResolved.getArgs().length - 1] != null; + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE + || onError.getArgs()[onError.getArgs().length - 1] != null; return promise; } @@ -354,4 +388,48 @@ protected boolean hasTagIgnoringEagerness(final Class tag) { } } + + @GenerateNodeFactory + @ImportStatic(PromisePrims.class) + @Primitive(primitive = "actorsGroupPromise:with:", selector = ",", + receiverType = SPromise.class) + public abstract static class GroupPromisesPrim extends BinarySystemOperation { + ForkJoinPool actorPool; + + public final GroupPromisesPrim initialize(final VM vm) { + super.initialize(vm); + actorPool = vm.getActorPool(); + return this; + } + @Specialization + public final SPromise groupPromises(final VirtualFrame frame, + final SPromise promise, final SPromise otherPromise) { + // cannot group a promise with itself + assert promise != otherPromise; + if (promise.isCompleted() && otherPromise.isCompleted()) { + Object[] resValues = new Object[]{promise.getValueForPromiseGroupResolution(), otherPromise.getValueForPromiseGroupResolution()}; + Object groupResValue = new SArray.SMutableArray(resValues,Classes.arrayClass); + return new SPromise(promise.getOwner(), false, false, SPromise.Resolution.SUCCESSFUL,groupResValue ); + } + + if(promise.isPromiseGroup){ + // add the promise to the promiseGroup + otherPromise.setPromiseGroupRoot(promise); + promise.addPromiseToGroup(otherPromise); + return promise; + } else if(otherPromise.isPromiseGroup){ + //add the promise to the promiseGroup + promise.setPromiseGroupRoot(otherPromise); + otherPromise.addPromiseToGroup(promise); + return otherPromise; + } else { + SPromise newPromise = new SPromise(promise.getOwner(), false, false); + promise.setPromiseGroupRoot(newPromise); + otherPromise.setPromiseGroupRoot(newPromise); + newPromise.addPromiseToGroup(promise); + newPromise.addPromiseToGroup(otherPromise); + return newPromise; + } + } + } } diff --git a/src/som/primitives/arrays/ArraySetAllStrategy.java b/src/som/primitives/arrays/ArraySetAllStrategy.java index b9dacd5ac7..dde0664978 100644 --- a/src/som/primitives/arrays/ArraySetAllStrategy.java +++ b/src/som/primitives/arrays/ArraySetAllStrategy.java @@ -17,11 +17,11 @@ public final class ArraySetAllStrategy { - public static void evalBlockForRemaining(final SBlock block, + public static void evalBlockForRemaining(final VirtualFrame frame, final SBlock block, final long length, final Object[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = blockDispatch.executeDispatch(new Object[] {block}); + storage[i] = blockDispatch.executeDispatch(frame, new Object[] {block}); } } @@ -30,65 +30,65 @@ public static void evalBlockWithArgForRemaining(final VirtualFrame frame, final BlockDispatchNode blockDispatch, final Object first, final IsValue isValue, final ExceptionSignalingNode notAValue) { if (!isValue.executeBoolean(frame, first)) { - notAValue.signal(Classes.valueArrayClass); + notAValue.signal(frame, Classes.valueArrayClass); } for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - Object result = blockDispatch.executeDispatch(new Object[] {block, (long) i + 1}); + Object result = blockDispatch.executeDispatch(frame, new Object[] {block, (long) i + 1}); if (!isValue.executeBoolean(frame, result)) { - notAValue.signal(Classes.valueArrayClass); + notAValue.signal(frame, Classes.valueArrayClass); } else { storage[i] = result; } } } - public static void evalBlockForRemaining(final SBlock block, + public static void evalBlockForRemaining(final VirtualFrame frame, final SBlock block, final long length, final long[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = (long) blockDispatch.executeDispatch(new Object[] {block}); + storage[i] = (long) blockDispatch.executeDispatch(frame, new Object[] {block}); } } - public static void evalBlockForRemaining(final SBlock block, + public static void evalBlockForRemaining(final VirtualFrame frame, final SBlock block, final long length, final double[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = (double) blockDispatch.executeDispatch(new Object[] {block}); + storage[i] = (double) blockDispatch.executeDispatch(frame, new Object[] {block}); } } - public static void evalBlockForRemaining(final SBlock block, + public static void evalBlockForRemaining(final VirtualFrame frame, final SBlock block, final long length, final boolean[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = (boolean) blockDispatch.executeDispatch(new Object[] {block}); + storage[i] = (boolean) blockDispatch.executeDispatch(frame, new Object[] {block}); } } - public static void evalBlockWithArgForRemaining(final SBlock block, + public static void evalBlockWithArgForRemaining(final VirtualFrame frame, final SBlock block, final long length, final long[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = (long) blockDispatch.executeDispatch( + storage[i] = (long) blockDispatch.executeDispatch(frame, new Object[] {block, (long) i + 1}); } } - public static void evalBlockWithArgForRemaining(final SBlock block, + public static void evalBlockWithArgForRemaining(final VirtualFrame frame, final SBlock block, final long length, final double[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = (double) blockDispatch.executeDispatch( + storage[i] = (double) blockDispatch.executeDispatch(frame, new Object[] {block, (long) i + 1}); } } - public static void evalBlockWithArgForRemaining(final SBlock block, + public static void evalBlockWithArgForRemaining(final VirtualFrame frame, final SBlock block, final long length, final boolean[] storage, final BlockDispatchNode blockDispatch) { for (int i = SArray.FIRST_IDX + 1; i < length; i++) { - storage[i] = (boolean) blockDispatch.executeDispatch( + storage[i] = (boolean) blockDispatch.executeDispatch(frame, new Object[] {block, (long) i + 1}); } } @@ -176,31 +176,31 @@ public static Object evalForRemainingNils(final VirtualFrame frame, return exprs.length; } - public static Object evaluateFirstDetermineStorageAndEvaluateRest( + public static Object evaluateFirstDetermineStorageAndEvaluateRest(final VirtualFrame frame, final SBlock blockNoArg, final long length, final BlockDispatchNode blockDispatch) { // TODO: this version does not handle the case that a subsequent value is // not of the expected type... - Object result = blockDispatch.executeDispatch(new Object[] {blockNoArg}); + Object result = blockDispatch.executeDispatch(frame, new Object[] {blockNoArg}); if (result instanceof Long) { long[] newStorage = new long[(int) length]; newStorage[0] = (long) result; - evalBlockForRemaining(blockNoArg, length, newStorage, blockDispatch); + evalBlockForRemaining(frame, blockNoArg, length, newStorage, blockDispatch); return newStorage; } else if (result instanceof Double) { double[] newStorage = new double[(int) length]; newStorage[0] = (double) result; - evalBlockForRemaining(blockNoArg, length, newStorage, blockDispatch); + evalBlockForRemaining(frame, blockNoArg, length, newStorage, blockDispatch); return newStorage; } else if (result instanceof Boolean) { boolean[] newStorage = new boolean[(int) length]; newStorage[0] = (boolean) result; - evalBlockForRemaining(blockNoArg, length, newStorage, blockDispatch); + evalBlockForRemaining(frame, blockNoArg, length, newStorage, blockDispatch); return newStorage; } else { Object[] newStorage = new Object[(int) length]; newStorage[0] = result; - evalBlockForRemaining(blockNoArg, length, newStorage, blockDispatch); + evalBlockForRemaining(frame, blockNoArg, length, newStorage, blockDispatch); return newStorage; } } @@ -211,22 +211,23 @@ public static Object evaluateFirstDetermineStorageAndEvaluateRest(final VirtualF final ExceptionSignalingNode notAValue) { // TODO: this version does not handle the case that a subsequent value is // not of the expected type... - Object result = blockDispatch.executeDispatch(new Object[] {blockWithArg, (long) 1}); + Object result = + blockDispatch.executeDispatch(frame, new Object[] {blockWithArg, (long) 1}); if (result instanceof Long) { long[] newStorage = new long[(int) length]; newStorage[0] = (long) result; - evalBlockWithArgForRemaining(blockWithArg, length, newStorage, blockDispatch); + evalBlockWithArgForRemaining(frame, blockWithArg, length, newStorage, blockDispatch); return newStorage; } else if (result instanceof Double) { double[] newStorage = new double[(int) length]; newStorage[0] = (double) result; - evalBlockWithArgForRemaining(blockWithArg, length, newStorage, blockDispatch); + evalBlockWithArgForRemaining(frame, blockWithArg, length, newStorage, blockDispatch); return newStorage; } else if (result instanceof Boolean) { boolean[] newStorage = new boolean[(int) length]; newStorage[0] = (boolean) result; - evalBlockWithArgForRemaining(blockWithArg, length, newStorage, blockDispatch); + evalBlockWithArgForRemaining(frame, blockWithArg, length, newStorage, blockDispatch); return newStorage; } else { Object[] newStorage = new Object[(int) length]; diff --git a/src/som/primitives/arrays/AtPrim.java b/src/som/primitives/arrays/AtPrim.java index f4b5f7e999..65fa66d281 100644 --- a/src/som/primitives/arrays/AtPrim.java +++ b/src/som/primitives/arrays/AtPrim.java @@ -85,62 +85,68 @@ protected boolean hasTagIgnoringEagerness(final Class tag) { } } - private Object triggerException(final SArray arr, final long idx) { + private Object triggerException(final VirtualFrame frame, final SArray arr, final long idx) { int rcvrIdx = SArguments.RCVR_IDX; assert rcvrIdx == 0; - return indexOutOfBounds.signal(arr, idx); + return indexOutOfBounds.signal(frame, arr, idx); } @Specialization(guards = "receiver.isEmptyType()") - public final Object doEmptySArray(final SArray receiver, final long idx) { + public final Object doEmptySArray(final VirtualFrame frame, final SArray receiver, + final long idx) { if (idx < 1 || idx > receiver.getEmptyStorage()) { - return triggerException(receiver, idx); + return triggerException(frame, receiver, idx); } return Nil.nilObject; } @Specialization(guards = "receiver.isPartiallyEmptyType()") - public final Object doPartiallyEmptySArray(final SArray receiver, final long idx) { + public final Object doPartiallyEmptySArray(final VirtualFrame frame, final SArray receiver, + final long idx) { try { return receiver.getPartiallyEmptyStorage().get(idx - 1); } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, idx); + return triggerException(frame, receiver, idx); } } @Specialization(guards = "receiver.isObjectType()") - public final Object doObjectSArray(final SArray receiver, final long idx) { + public final Object doObjectSArray(final VirtualFrame frame, final SArray receiver, + final long idx) { try { return receiver.getObjectStorage()[(int) idx - 1]; } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, idx); + return triggerException(frame, receiver, idx); } } @Specialization(guards = "receiver.isLongType()") - public final long doLongSArray(final SArray receiver, final long idx) { + public final long doLongSArray(final VirtualFrame frame, final SArray receiver, + final long idx) { try { return receiver.getLongStorage()[(int) idx - 1]; } catch (IndexOutOfBoundsException e) { - return (long) triggerException(receiver, idx); + return (long) triggerException(frame, receiver, idx); } } @Specialization(guards = "receiver.isDoubleType()") - public final double doDoubleSArray(final SArray receiver, final long idx) { + public final double doDoubleSArray(final VirtualFrame frame, final SArray receiver, + final long idx) { try { return receiver.getDoubleStorage()[(int) idx - 1]; } catch (IndexOutOfBoundsException e) { - return (double) triggerException(receiver, idx); + return (double) triggerException(frame, receiver, idx); } } @Specialization(guards = "receiver.isBooleanType()") - public final boolean doBooleanSArray(final SArray receiver, final long idx) { + public final boolean doBooleanSArray(final VirtualFrame frame, final SArray receiver, + final long idx) { try { return receiver.getBooleanStorage()[(int) idx - 1]; } catch (IndexOutOfBoundsException e) { - return (boolean) triggerException(receiver, idx); + return (boolean) triggerException(frame, receiver, idx); } } } diff --git a/src/som/primitives/arrays/AtPutPrim.java b/src/som/primitives/arrays/AtPutPrim.java index 92221870c9..8491804e8e 100644 --- a/src/som/primitives/arrays/AtPutPrim.java +++ b/src/som/primitives/arrays/AtPutPrim.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.SourceSection; @@ -108,10 +109,10 @@ protected static final boolean valueNotLongDoubleBoolean(final Object value) { !(value instanceof Boolean); } - private Object triggerException(final SArray arr, final long idx) { + private Object triggerException(final VirtualFrame frame, final SArray arr, final long idx) { int rcvrIdx = SArguments.RCVR_IDX; assert rcvrIdx == 0; - return indexOutOfBounds.signal(arr, idx); + return indexOutOfBounds.signal(frame, arr, idx); } private static void setValue(final long idx, final Object value, @@ -140,41 +141,45 @@ private void setAndPossiblyTransition(final SMutableArray receiver, } @Specialization(guards = {"receiver.isEmptyType()"}) - public final long doEmptySArray(final SMutableArray receiver, final long index, + public final long doEmptySArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final long value) { try { receiver.transitionFromEmptyToPartiallyEmptyWith(index - 1, value); return value; } catch (IndexOutOfBoundsException e) { - return (long) triggerException(receiver, index); + return (long) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isEmptyType()"}) - public final double doEmptySArray(final SMutableArray receiver, final long index, + public final double doEmptySArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final double value) { try { receiver.transitionFromEmptyToPartiallyEmptyWith(index - 1, value); return value; } catch (IndexOutOfBoundsException e) { - return (double) triggerException(receiver, index); + return (double) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isEmptyType()"}) - public final boolean doEmptySArray(final SMutableArray receiver, final long index, + public final boolean doEmptySArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final boolean value) { try { receiver.transitionFromEmptyToPartiallyEmptyWith(index - 1, value); return value; } catch (IndexOutOfBoundsException e) { - return (boolean) triggerException(receiver, index); + return (boolean) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isEmptyType()", "valueIsNotNil(value)", "valueNotLongDoubleBoolean(value)"}) - public final Object doEmptySArray(final SMutableArray receiver, final long index, + public final Object doEmptySArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final Object value) { final int idx = (int) index - 1; int size = receiver.getEmptyStorage(); @@ -187,55 +192,60 @@ public final Object doEmptySArray(final SMutableArray receiver, final long index receiver.transitionTo(newStorage); return value; } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isEmptyType()", "valueIsNil(value)"}) - public final Object doEmptySArrayWithNil(final SMutableArray receiver, final long index, + public final Object doEmptySArrayWithNil(final VirtualFrame frame, + final SMutableArray receiver, final long index, final Object value) { long idx = index - 1; if (idx < 0 || idx >= receiver.getEmptyStorage()) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } return Nil.nilObject; } @Specialization(guards = "receiver.isPartiallyEmptyType()") - public final long doPartiallyEmptySArray(final SMutableArray receiver, final long index, + public final long doPartiallyEmptySArray(final VirtualFrame frame, + final SMutableArray receiver, final long index, final long value) { try { setAndPossiblyTransition(receiver, index, value, PartiallyEmptyArray.Type.LONG); return value; } catch (IndexOutOfBoundsException e) { - return (long) triggerException(receiver, index); + return (long) triggerException(frame, receiver, index); } } @Specialization(guards = "receiver.isPartiallyEmptyType()") - public final double doPartiallyEmptySArray(final SMutableArray receiver, final long index, + public final double doPartiallyEmptySArray(final VirtualFrame frame, + final SMutableArray receiver, final long index, final double value) { try { setAndPossiblyTransition(receiver, index, value, PartiallyEmptyArray.Type.DOUBLE); return value; } catch (IndexOutOfBoundsException e) { - return (double) triggerException(receiver, index); + return (double) triggerException(frame, receiver, index); } } @Specialization(guards = "receiver.isPartiallyEmptyType()") - public final boolean doPartiallyEmptySArray(final SMutableArray receiver, final long index, + public final boolean doPartiallyEmptySArray(final VirtualFrame frame, + final SMutableArray receiver, final long index, final boolean value) { try { setAndPossiblyTransition(receiver, index, value, PartiallyEmptyArray.Type.BOOLEAN); return value; } catch (IndexOutOfBoundsException e) { - return (boolean) triggerException(receiver, index); + return (boolean) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isPartiallyEmptyType()", "valueIsNil(value)"}) - public final Object doPartiallyEmptySArrayWithNil(final SMutableArray receiver, + public final Object doPartiallyEmptySArrayWithNil(final VirtualFrame frame, + final SMutableArray receiver, final long index, final Object value) { long idx = index - 1; PartiallyEmptyArray storage = receiver.getPartiallyEmptyStorage(); @@ -247,45 +257,49 @@ public final Object doPartiallyEmptySArrayWithNil(final SMutableArray receiver, } return value; } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isPartiallyEmptyType()", "valueIsNotNil(value)"}) - public final Object doPartiallyEmptySArray(final SMutableArray receiver, final long index, + public final Object doPartiallyEmptySArray(final VirtualFrame frame, + final SMutableArray receiver, final long index, final Object value) { try { setAndPossiblyTransition(receiver, index, value, PartiallyEmptyArray.Type.OBJECT); return value; } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } @Specialization(guards = "receiver.isObjectType()") - public final Object doObjectSArray(final SMutableArray receiver, final long index, + public final Object doObjectSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final Object value) { try { receiver.getObjectStorage()[(int) index - 1] = value; return value; } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } @Specialization(guards = "receiver.isLongType()") - public final long doObjectSArray(final SMutableArray receiver, final long index, + public final long doObjectSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final long value) { try { receiver.getLongStorage()[(int) index - 1] = value; return value; } catch (IndexOutOfBoundsException e) { - return (long) triggerException(receiver, index); + return (long) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isLongType()", "valueIsNotLong(value)"}) - public final Object doLongSArray(final SMutableArray receiver, final long index, + public final Object doLongSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final Object value) { long[] storage = receiver.getLongStorage(); Object[] newStorage = new Object[storage.length]; @@ -296,23 +310,25 @@ public final Object doLongSArray(final SMutableArray receiver, final long index, try { return transitionAndSet(receiver, index, value, newStorage); } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } @Specialization(guards = "receiver.isDoubleType()") - public final double doDoubleSArray(final SMutableArray receiver, final long index, + public final double doDoubleSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final double value) { try { receiver.getDoubleStorage()[(int) index - 1] = value; return value; } catch (IndexOutOfBoundsException e) { - return (double) triggerException(receiver, index); + return (double) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isDoubleType()", "valueIsNotDouble(value)"}) - public final Object doDoubleSArray(final SMutableArray receiver, final long index, + public final Object doDoubleSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final Object value) { double[] storage = receiver.getDoubleStorage(); Object[] newStorage = new Object[storage.length]; @@ -322,23 +338,25 @@ public final Object doDoubleSArray(final SMutableArray receiver, final long inde try { return transitionAndSet(receiver, index, value, newStorage); } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } @Specialization(guards = "receiver.isBooleanType()") - public final boolean doBooleanSArray(final SMutableArray receiver, final long index, + public final boolean doBooleanSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final boolean value) { try { receiver.getBooleanStorage()[(int) index - 1] = value; return value; } catch (IndexOutOfBoundsException e) { - return (boolean) triggerException(receiver, index); + return (boolean) triggerException(frame, receiver, index); } } @Specialization(guards = {"receiver.isBooleanType()", "valueIsNotBoolean(value)"}) - public final Object doBooleanSArray(final SMutableArray receiver, final long index, + public final Object doBooleanSArray(final VirtualFrame frame, final SMutableArray receiver, + final long index, final Object value) { boolean[] storage = receiver.getBooleanStorage(); Object[] newStorage = new Object[storage.length]; @@ -348,7 +366,7 @@ public final Object doBooleanSArray(final SMutableArray receiver, final long ind try { return transitionAndSet(receiver, index, value, newStorage); } catch (IndexOutOfBoundsException e) { - return triggerException(receiver, index); + return triggerException(frame, receiver, index); } } } diff --git a/src/som/primitives/arrays/CopyPrim.java b/src/som/primitives/arrays/CopyPrim.java index 3e02a2a2d3..f0146af2d8 100644 --- a/src/som/primitives/arrays/CopyPrim.java +++ b/src/som/primitives/arrays/CopyPrim.java @@ -16,16 +16,14 @@ public abstract class CopyPrim extends UnaryExpressionNode { @Specialization(guards = "receiver.isEmptyType()") public final SMutableArray doEmptyArray(final SMutableArray receiver) { assert !receiver.getSOMClass() - .isTransferObject() - : "Not yet supported, need to instantiate another class"; + .isTransferObject() : "Not yet supported, need to instantiate another class"; return new SMutableArray(receiver.getEmptyStorage(), receiver.getSOMClass()); } @Specialization(guards = "receiver.isPartiallyEmptyType()") public final SMutableArray doPartiallyEmptyArray(final SMutableArray receiver) { assert !receiver.getSOMClass() - .isTransferObject() - : "Not yet supported, need to instantiate another class"; + .isTransferObject() : "Not yet supported, need to instantiate another class"; return new SMutableArray(receiver.getPartiallyEmptyStorage().copy(), receiver.getSOMClass()); } @@ -33,8 +31,7 @@ public final SMutableArray doPartiallyEmptyArray(final SMutableArray receiver) { @Specialization(guards = "receiver.isObjectType()") public final SMutableArray doObjectArray(final SMutableArray receiver) { assert !receiver.getSOMClass() - .isTransferObject() - : "Not yet supported, need to instantiate another class"; + .isTransferObject() : "Not yet supported, need to instantiate another class"; return new SMutableArray(receiver.getObjectStorage().clone(), receiver.getSOMClass()); } @@ -42,8 +39,7 @@ public final SMutableArray doObjectArray(final SMutableArray receiver) { @Specialization(guards = "receiver.isLongType()") public final SMutableArray doLongArray(final SMutableArray receiver) { assert !receiver.getSOMClass() - .isTransferObject() - : "Not yet supported, need to instantiate another class"; + .isTransferObject() : "Not yet supported, need to instantiate another class"; return new SMutableArray(receiver.getLongStorage().clone(), receiver.getSOMClass()); } @@ -51,8 +47,7 @@ public final SMutableArray doLongArray(final SMutableArray receiver) { @Specialization(guards = "receiver.isDoubleType()") public final SMutableArray doDoubleArray(final SMutableArray receiver) { assert !receiver.getSOMClass() - .isTransferObject() - : "Not yet supported, need to instantiate another class"; + .isTransferObject() : "Not yet supported, need to instantiate another class"; return new SMutableArray(receiver.getDoubleStorage().clone(), receiver.getSOMClass()); } @@ -60,8 +55,7 @@ public final SMutableArray doDoubleArray(final SMutableArray receiver) { @Specialization(guards = "receiver.isBooleanType()") public final SMutableArray doBooleanArray(final SMutableArray receiver) { assert !receiver.getSOMClass() - .isTransferObject() - : "Not yet supported, need to instantiate another class"; + .isTransferObject() : "Not yet supported, need to instantiate another class"; return new SMutableArray(receiver.getBooleanStorage().clone(), receiver.getSOMClass()); } diff --git a/src/som/primitives/arrays/DoIndexesPrim.java b/src/som/primitives/arrays/DoIndexesPrim.java index 6bfc66f776..223b29a6a9 100644 --- a/src/som/primitives/arrays/DoIndexesPrim.java +++ b/src/som/primitives/arrays/DoIndexesPrim.java @@ -7,6 +7,7 @@ import com.oracle.truffle.api.source.SourceSection; import bd.primitives.Primitive; +import som.interpreter.SArguments; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.dispatch.BlockDispatchNode; import som.interpreter.nodes.dispatch.BlockDispatchNodeGen; @@ -16,13 +17,16 @@ import som.primitives.SizeAndLengthPrimFactory; import som.vmobjects.SArray; import som.vmobjects.SBlock; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; @GenerateNodeFactory @Primitive(selector = "doIndexes:", receiverType = SArray.class, disabled = true) public abstract class DoIndexesPrim extends BinaryComplexOperation { - @Child protected BlockDispatchNode block = BlockDispatchNodeGen.create(); - @Child protected UnaryExpressionNode length; + @Child protected BlockDispatchNode block = BlockDispatchNodeGen.create(); + @Child protected UnaryExpressionNode length; + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); + // TODO: tag properly, this is a loop, but without array access @Override @@ -38,23 +42,26 @@ public DoIndexesPrim initialize(final SourceSection sourceSection) { public final SArray doArray(final VirtualFrame frame, final SArray receiver, final SBlock block) { int length = (int) (long) this.length.executeEvaluated(frame, receiver); - loop(block, length); + loop(frame, block, length); return receiver; } - private void loop(final SBlock block, final int length) { + private void loop(final VirtualFrame frame, final SBlock block, final int length) { try { int expectedFirstIdx = 0; // this code is written with this expectation assert SArray.FIRST_IDX == expectedFirstIdx; if (SArray.FIRST_IDX < length) { - this.block.executeDispatch(new Object[] { - // +1 because it is going to the smalltalk level - block, (long) SArray.FIRST_IDX + 1}); + this.block.executeDispatch(frame, SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, block, (long) SArray.FIRST_IDX + 1)); // +1 because it + // is going to + // the smalltalk + // level } for (long i = 1; i < length; i++) { - this.block.executeDispatch(new Object[] { - block, i + 1}); // +1 because it is going to the smalltalk level + this.block.executeDispatch(frame, SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, block, i + 1)); // +1 because it is going to the + // smalltalk level } } finally { if (CompilerDirectives.inInterpreter()) { diff --git a/src/som/primitives/arrays/DoPrim.java b/src/som/primitives/arrays/DoPrim.java index 07e95b9ce9..75150bc1f1 100644 --- a/src/som/primitives/arrays/DoPrim.java +++ b/src/som/primitives/arrays/DoPrim.java @@ -3,8 +3,10 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import bd.primitives.Primitive; +import som.interpreter.SArguments; import som.interpreter.nodes.ExpressionNode; import som.interpreter.nodes.dispatch.BlockDispatchNode; import som.interpreter.nodes.dispatch.BlockDispatchNodeGen; @@ -14,28 +16,32 @@ import som.vmobjects.SArray; import som.vmobjects.SArray.PartiallyEmptyArray; import som.vmobjects.SBlock; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; @GenerateNodeFactory @Primitive(selector = "do:", receiverType = SArray.class, disabled = true) public abstract class DoPrim extends BinaryComplexOperation { - @Child private BlockDispatchNode block = BlockDispatchNodeGen.create(); + @Child private BlockDispatchNode block = BlockDispatchNodeGen.create(); + @Child protected ShadowStackEntryLoad shadowStackEntryLoad = ShadowStackEntryLoad.create(); // TODO: tag properly, it is a loop and an access - private void execBlock(final SBlock block, final Object arg) { - this.block.executeDispatch(new Object[] {block, arg}); + private void execBlock(final VirtualFrame frame, final SBlock block, final Object arg) { + this.block.executeDispatch(frame, SArguments.getPlainXArgumentsWithReceiver(this, + shadowStackEntryLoad, frame, block, arg)); } @Specialization(guards = "arr.isEmptyType()") - public final SArray doEmptyArray(final SArray arr, final SBlock block) { + public final SArray doEmptyArray(final VirtualFrame frame, final SArray arr, + final SBlock block) { int length = arr.getEmptyStorage(); try { if (SArray.FIRST_IDX < length) { - execBlock(block, Nil.nilObject); + execBlock(frame, block, Nil.nilObject); } for (long i = SArray.FIRST_IDX + 1; i < length; i++) { - execBlock(block, Nil.nilObject); + execBlock(frame, block, Nil.nilObject); } } finally { if (CompilerDirectives.inInterpreter()) { @@ -46,15 +52,16 @@ public final SArray doEmptyArray(final SArray arr, final SBlock block) { } @Specialization(guards = "arr.isPartiallyEmptyType()") - public final SArray doPartiallyEmptyArray(final SArray arr, final SBlock block) { + public final SArray doPartiallyEmptyArray(final VirtualFrame frame, final SArray arr, + final SBlock block) { PartiallyEmptyArray storage = arr.getPartiallyEmptyStorage(); int length = storage.getLength(); try { if (SArray.FIRST_IDX < length) { - execBlock(block, storage.get(SArray.FIRST_IDX)); + execBlock(frame, block, storage.get(SArray.FIRST_IDX)); } for (long i = SArray.FIRST_IDX + 1; i < length; i++) { - execBlock(block, storage.get(i)); + execBlock(frame, block, storage.get(i)); } } finally { if (CompilerDirectives.inInterpreter()) { @@ -65,15 +72,16 @@ public final SArray doPartiallyEmptyArray(final SArray arr, final SBlock block) } @Specialization(guards = "arr.isObjectType()") - public final SArray doObjectArray(final SArray arr, final SBlock block) { + public final SArray doObjectArray(final VirtualFrame frame, final SArray arr, + final SBlock block) { Object[] storage = arr.getObjectStorage(); int length = storage.length; try { if (SArray.FIRST_IDX < length) { - execBlock(block, storage[SArray.FIRST_IDX]); + execBlock(frame, block, storage[SArray.FIRST_IDX]); } for (long i = SArray.FIRST_IDX + 1; i < length; i++) { - execBlock(block, storage[(int) i]); + execBlock(frame, block, storage[(int) i]); } } finally { if (CompilerDirectives.inInterpreter()) { @@ -84,15 +92,16 @@ public final SArray doObjectArray(final SArray arr, final SBlock block) { } @Specialization(guards = "arr.isLongType()") - public final SArray doLongArray(final SArray arr, final SBlock block) { + public final SArray doLongArray(final VirtualFrame frame, final SArray arr, + final SBlock block) { long[] storage = arr.getLongStorage(); int length = storage.length; try { if (SArray.FIRST_IDX < length) { - execBlock(block, storage[SArray.FIRST_IDX]); + execBlock(frame, block, storage[SArray.FIRST_IDX]); } for (long i = SArray.FIRST_IDX + 1; i < length; i++) { - execBlock(block, storage[(int) i]); + execBlock(frame, block, storage[(int) i]); } } finally { if (CompilerDirectives.inInterpreter()) { @@ -103,15 +112,16 @@ public final SArray doLongArray(final SArray arr, final SBlock block) { } @Specialization(guards = "arr.isDoubleType()") - public final SArray doDoubleArray(final SArray arr, final SBlock block) { + public final SArray doDoubleArray(final VirtualFrame frame, final SArray arr, + final SBlock block) { double[] storage = arr.getDoubleStorage(); int length = storage.length; try { if (SArray.FIRST_IDX < length) { - execBlock(block, storage[SArray.FIRST_IDX]); + execBlock(frame, block, storage[SArray.FIRST_IDX]); } for (long i = SArray.FIRST_IDX + 1; i < length; i++) { - execBlock(block, storage[(int) i]); + execBlock(frame, block, storage[(int) i]); } } finally { if (CompilerDirectives.inInterpreter()) { @@ -122,15 +132,16 @@ public final SArray doDoubleArray(final SArray arr, final SBlock block) { } @Specialization(guards = "arr.isBooleanType()") - public final SArray doBooleanArray(final SArray arr, final SBlock block) { + public final SArray doBooleanArray(final VirtualFrame frame, final SArray arr, + final SBlock block) { boolean[] storage = arr.getBooleanStorage(); int length = storage.length; try { if (SArray.FIRST_IDX < length) { - execBlock(block, storage[SArray.FIRST_IDX]); + execBlock(frame, block, storage[SArray.FIRST_IDX]); } for (long i = SArray.FIRST_IDX + 1; i < length; i++) { - execBlock(block, storage[(int) i]); + execBlock(frame, block, storage[(int) i]); } } finally { if (CompilerDirectives.inInterpreter()) { diff --git a/src/som/primitives/arrays/PutAllNode.java b/src/som/primitives/arrays/PutAllNode.java index 40f4fb02c0..aceaba2c42 100644 --- a/src/som/primitives/arrays/PutAllNode.java +++ b/src/som/primitives/arrays/PutAllNode.java @@ -5,6 +5,7 @@ import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import bd.primitives.Primitive; import som.interpreter.nodes.dispatch.BlockDispatchNode; @@ -51,15 +52,16 @@ public SMutableArray doPutNilInOtherArray(final SMutableArray rcvr, } @Specialization - public SMutableArray doPutEvalBlock(final SMutableArray rcvr, + public SMutableArray doPutEvalBlock(VirtualFrame frame, final SMutableArray rcvr, final SBlock block, final long length) { if (length <= 0) { return rcvr; } try { - Object newStorage = ArraySetAllStrategy.evaluateFirstDetermineStorageAndEvaluateRest( - block, length, this.block); + Object newStorage = + ArraySetAllStrategy.evaluateFirstDetermineStorageAndEvaluateRest(frame, + block, length, this.block); rcvr.transitionTo(newStorage); } finally { if (CompilerDirectives.inInterpreter()) { diff --git a/src/som/primitives/processes/ChannelPrimitives.java b/src/som/primitives/processes/ChannelPrimitives.java index 7899dc238e..fa83a16dc8 100644 --- a/src/som/primitives/processes/ChannelPrimitives.java +++ b/src/som/primitives/processes/ChannelPrimitives.java @@ -40,11 +40,11 @@ import tools.concurrency.Tags.ExpressionBreakpoint; import tools.concurrency.TracingActivityThread; import tools.debugger.WebDebugger; +import tools.debugger.breakpoints.Breakpoints; import tools.debugger.entities.ActivityType; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.PassiveEntityType; import tools.debugger.nodes.AbstractBreakpointNode; -import tools.debugger.session.Breakpoints; import tools.replay.ReplayRecord; import tools.replay.TraceParser; import tools.replay.TraceRecord; @@ -123,7 +123,11 @@ public void run() { Symbols.symbolFor("run"), AccessModifier.PROTECTED); beforeExec(disp); - disp.invoke(new Object[] {obj}); + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + disp.invoke(new Object[] {obj, null}); + } else { + disp.invoke(new Object[] {obj}); + } } catch (Throwable t) { t.printStackTrace(); } finally { @@ -316,7 +320,7 @@ public final WritePrim initialize(final VM vm) { public final Object write(final VirtualFrame frame, final SChannelOutput out, final Object val) { if (!isVal.executeBoolean(frame, val)) { - notAValue.signal(val); + notAValue.signal(frame, val); } try { out.writeAndSuspendReader(val, afterRead.executeShouldHalt(), traceWrite); diff --git a/src/som/primitives/reflection/AbstractSymbolDispatch.java b/src/som/primitives/reflection/AbstractSymbolDispatch.java index 982411c375..3c4e40395f 100644 --- a/src/som/primitives/reflection/AbstractSymbolDispatch.java +++ b/src/som/primitives/reflection/AbstractSymbolDispatch.java @@ -7,13 +7,16 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.CompilerDirectives; import bd.primitives.nodes.PreevaluatedExpression; import som.compiler.AccessModifier; +import som.interpreter.SArguments; import som.interpreter.SomLanguage; import som.interpreter.Types; import som.interpreter.nodes.MessageSendNode; import som.interpreter.nodes.MessageSendNode.AbstractMessageSendNode; +import som.interpreter.nodes.dispatch.BackCacheCallNode; import som.interpreter.nodes.dispatch.Dispatchable; import som.interpreter.nodes.dispatch.GenericDispatchNode; import som.primitives.arrays.ToArgumentsArrayNode; @@ -22,6 +25,9 @@ import som.vmobjects.SArray; import som.vmobjects.SClass; import som.vmobjects.SSymbol; +import tools.debugger.asyncstacktraces.ShadowStackEntryLoad; + +import java.util.Arrays; public abstract class AbstractSymbolDispatch extends Node { @@ -66,8 +72,12 @@ public Object doCachedWithoutArgArr(final VirtualFrame frame, final Object receiver, final SSymbol selector, final Object argsArr, @Cached("selector") final SSymbol cachedSelector, @Cached("createForPerformNodes(selector)") final AbstractMessageSendNode cachedSend) { - Object[] arguments = {receiver}; - + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {receiver, null}; + } else { + arguments = new Object[] {receiver}; + } PreevaluatedExpression realCachedSend = cachedSend; return realCachedSend.doPreEvaluated(frame, arguments); } @@ -79,20 +89,34 @@ public Object doCached(final VirtualFrame frame, @Cached("createForPerformNodes(selector)") final AbstractMessageSendNode cachedSend, @Cached("createArgArrayNode()") final ToArgumentsArrayNode toArgArray) { Object[] arguments = toArgArray.executedEvaluated(argsArr, receiver); + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = Arrays.copyOf(arguments, arguments.length + 1); + arguments[arguments.length - 1] = null; + } PreevaluatedExpression realCachedSend = cachedSend; return realCachedSend.doPreEvaluated(frame, arguments); } @Specialization(replaces = "doCachedWithoutArgArr", guards = "argsArr == null") - @TruffleBoundary - public Object doUncached(final Object receiver, final SSymbol selector, + // @TruffleBoundary + public Object doUncached(final VirtualFrame frame, final Object receiver, + final SSymbol selector, final Object argsArr, - @Cached("create()") final IndirectCallNode call) { + @Cached("create()") final IndirectCallNode call, + @Cached ShadowStackEntryLoad shadowStackEntryLoad) { + CompilerDirectives.transferToInterpreter(); SClass rcvrClass = Types.getClassOf(receiver); Dispatchable invokable = rcvrClass.lookupMessage(selector, AccessModifier.PUBLIC); + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {receiver, null}; + BackCacheCallNode.setShadowStackEntry(frame, false, arguments, this, + shadowStackEntryLoad); + } else { + arguments = new Object[] {receiver}; + } - Object[] arguments = {receiver}; if (invokable != null) { return invokable.invoke(call, arguments); } else { diff --git a/src/som/primitives/threading/MutexPrimitives.java b/src/som/primitives/threading/MutexPrimitives.java index 7f93623d15..df0c1a34a3 100644 --- a/src/som/primitives/threading/MutexPrimitives.java +++ b/src/som/primitives/threading/MutexPrimitives.java @@ -7,6 +7,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.api.frame.VirtualFrame; import bd.primitives.Primitive; import som.interpreter.nodes.dispatch.BlockDispatchNode; @@ -102,10 +103,11 @@ public abstract static class CritialPrim extends BinaryExpressionNode { @Child protected BlockDispatchNode dispatchBody = BlockDispatchNodeGen.create(); @Specialization - public Object critical(final ReentrantLock lock, final SBlock block) { + public Object critical(final VirtualFrame frame, final ReentrantLock lock, + final SBlock block) { LockPrim.lock(lock); try { - return dispatchBody.executeDispatch(new Object[] {block}); + return dispatchBody.executeDispatch(frame, new Object[] {block}); } finally { UnlockPrim.unlock(lock); } diff --git a/src/som/primitives/threading/TaskThreads.java b/src/som/primitives/threading/TaskThreads.java index af4e859c83..ee6e971675 100644 --- a/src/som/primitives/threading/TaskThreads.java +++ b/src/som/primitives/threading/TaskThreads.java @@ -1,5 +1,7 @@ package som.primitives.threading; +import java.lang.reflect.Array; +import java.util.Arrays; import java.util.LinkedList; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; @@ -10,6 +12,7 @@ import som.VM; import som.interop.SomInteropObject; +import som.interpreter.SArguments; import som.interpreter.SomLanguage; import som.interpreter.objectstorage.ObjectTransitionSafepoint; import som.vm.Activity; @@ -19,6 +22,7 @@ import tools.concurrency.KomposTrace; import tools.concurrency.TracingActivityThread; import tools.debugger.WebDebugger; +import tools.debugger.asyncstacktraces.ShadowStackEntry; import tools.debugger.entities.ActivityType; import tools.replay.ReplayRecord; import tools.replay.TraceParser; @@ -39,8 +43,7 @@ public abstract static class SomTaskOrThread extends RecursiveTask public SomTaskOrThread(final Object[] argArray, final boolean stopOnRoot) { this.argArray = argArray; this.stopOnRoot = stopOnRoot; - assert argArray[0] instanceof SBlock - : "First argument of a block needs to be the block object"; + assert argArray[0] instanceof SBlock : "First argument of a block needs to be the block object"; } public final SInvokable getMethod() { @@ -75,6 +78,14 @@ protected final Object compute() { ForkJoinThread thread = (ForkJoinThread) Thread.currentThread(); thread.task = this; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + if (!(argArray[argArray.length - 1] instanceof ShadowStackEntry)) { + Object[] arguments = Arrays.copyOf(argArray, argArray.length + 1); + arguments[argArray.length] = + SArguments.instantiateTopShadowStackEntry(target.getRootNode()); + return target.call(arguments); + } + } return target.call(argArray); } finally { ObjectTransitionSafepoint.INSTANCE.unregister(); diff --git a/src/som/primitives/transactions/AtomicPrim.java b/src/som/primitives/transactions/AtomicPrim.java index aa68c1c9b7..0ff01380c3 100644 --- a/src/som/primitives/transactions/AtomicPrim.java +++ b/src/som/primitives/transactions/AtomicPrim.java @@ -8,6 +8,7 @@ import bd.primitives.Primitive; import som.VM; +import som.interpreter.SArguments; import som.interpreter.actors.SuspendExecutionNodeGen; import som.interpreter.nodes.nary.BinaryComplexOperation.BinarySystemOperation; import som.interpreter.nodes.nary.UnaryExpressionNode; @@ -18,11 +19,11 @@ import tools.concurrency.Tags.Atomic; import tools.concurrency.Tags.ExpressionBreakpoint; import tools.concurrency.TracingActivityThread; +import tools.debugger.breakpoints.Breakpoints; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.EntityType; import tools.debugger.entities.SteppingType; import tools.debugger.nodes.AbstractBreakpointNode; -import tools.debugger.session.Breakpoints; import tools.replay.TraceRecord; import tools.replay.nodes.RecordEventNodes.RecordOneEvent; @@ -67,8 +68,13 @@ public final Object atomic(final VirtualFrame frame, final SClass clazz, vm.getWebDebugger().prepareSteppingAfterNextRootNode(Thread.currentThread()); } } - - Object result = block.getMethod().getAtomicCallTarget().call(new Object[] {block}); + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {block, SArguments.getShadowStackEntry(frame)}; + } else { + arguments = new Object[] {block}; + } + Object result = block.getMethod().getAtomicCallTarget().call(arguments); if (VmSettings.TRUFFLE_DEBUGGER_ENABLED && SteppingType.STEP_TO_COMMIT.isSet()) { diff --git a/src/som/vm/ExtensionLoader.java b/src/som/vm/ExtensionLoader.java index d493110210..78e23aea5b 100644 --- a/src/som/vm/ExtensionLoader.java +++ b/src/som/vm/ExtensionLoader.java @@ -110,8 +110,7 @@ protected void registerPrimitive( private SInvokable constructPrimitive(final SSymbol signature, final Specializer specializer, final SomLanguage lang) { CompilerAsserts.neverPartOfCompilation("This is only executed during bootstrapping."); - assert signature.getNumberOfSignatureArguments() >= 1 - : "Primitives should have at least a receiver"; + assert signature.getNumberOfSignatureArguments() >= 1 : "Primitives should have at least a receiver"; // ignore the implicit vmMirror argument final int numArgs = signature.getNumberOfSignatureArguments(); diff --git a/src/som/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java index abf9a8ce35..139700653c 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -30,6 +30,7 @@ import som.compiler.SourcecodeCompiler; import som.compiler.Variable; import som.interpreter.LexicalScope.MixinScope; +import som.interpreter.SArguments; import som.interpreter.SomLanguage; import som.interpreter.actors.Actor; import som.interpreter.actors.EventualMessage.DirectMessage; @@ -132,7 +133,7 @@ public SClass loadExtensionModule(final String filename) { ExtensionLoader loader = new ExtensionLoader(filename, compiler.getLanguage()); EconomicMap primitives = loader.getPrimitives(); MixinDefinition mixin = constructPrimitiveMixin(filename, primitives); - return mixin.instantiateClass(Nil.nilObject, Classes.topClass); + return mixin.instantiateClass(null, Nil.nilObject, Classes.topClass); } public MixinDefinition loadModule(final String filename) throws IOException { @@ -209,7 +210,7 @@ null, new MixinDefinitionId(Symbols.VMMIRROR), AccessModifier.PUBLIC, scope, sco true, true, true, null); scope.setMixinDefinition(vmMirrorDef, false); - SClass vmMirrorClass = vmMirrorDef.instantiateClass(Nil.nilObject, + SClass vmMirrorClass = vmMirrorDef.instantiateClass(null, Nil.nilObject, new SClass[] {Classes.topClass, Classes.valueClass}); return vmMirrorClass; } @@ -418,7 +419,8 @@ public SObjectWithoutFields initialize() { setDummyClassFactory(Classes.methodClass, SInvokableSerializationNodeFactory.getInstance()); - SClass kernelClass = kernelModule.instantiateClass(Nil.nilObject, Classes.objectClass); + SClass kernelClass = + kernelModule.instantiateClass(null, Nil.nilObject, Classes.objectClass); KernelObj.kernel.setClass(kernelClass); // create and initialize the vmMirror object @@ -527,7 +529,14 @@ public int executeApplication(final SObjectWithoutFields vmMirror, final Actor m mainThreadCompleted = new CompletableFuture<>(); ObjectTransitionSafepoint.INSTANCE.register(); - Object platform = platformModule.instantiateObject(platformClass, vmMirror); + Object platform; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + platform = platformModule.instantiateObject(platformClass, vmMirror, + SArguments.instantiateTopShadowStackEntry(null)); + } else { + platform = platformModule.instantiateObject(platformClass, vmMirror); + } + ObjectTransitionSafepoint.INSTANCE.unregister(); SSymbol start = Symbols.symbolFor("start"); @@ -542,8 +551,10 @@ public int executeApplication(final SObjectWithoutFields vmMirror, final Actor m "ObjectSystem.executeApplication").createSection(1); } + Object[] args = SArguments.convertToArgumentArray(new Object[] {platform}); + DirectMessage msg = new DirectMessage(mainActor, start, - new Object[] {platform}, mainActor, + args, mainActor, null, EventualSendNode.createOnReceiveCallTargetForVMMain( start, 1, source, mainThreadCompleted, compiler.getLanguage())); mainActor.sendInitialStartMessage(msg, vm.getActorPool()); @@ -573,7 +584,7 @@ public Object execute(final String selector) { Symbols.symbolFor(selector), AccessModifier.PUBLIC); try { ObjectTransitionSafepoint.INSTANCE.register(); - return method.invoke(new Object[] {platformClass}); + return method.invoke(SArguments.convertToArgumentArray(new Object[] {platformClass})); } finally { ObjectTransitionSafepoint.INSTANCE.unregister(); } diff --git a/src/som/vm/Primitives.java b/src/som/vm/Primitives.java index 20f475b352..72f6c0fdff 100644 --- a/src/som/vm/Primitives.java +++ b/src/som/vm/Primitives.java @@ -125,9 +125,8 @@ public Primitives(final SomLanguage lang) { private static SInvokable constructVmMirrorPrimitive(final SSymbol signature, final Specializer specializer, final SomLanguage lang) { CompilerAsserts.neverPartOfCompilation("This is only executed during bootstrapping."); - assert signature.getNumberOfSignatureArguments() > 1 - : "Primitives should have the vmMirror as receiver, " - + "and then at least one object they are applied to"; + assert signature.getNumberOfSignatureArguments() > 1 : "Primitives should have the vmMirror as receiver, " + + "and then at least one object they are applied to"; // ignore the implicit vmMirror argument final int numArgs = signature.getNumberOfSignatureArguments() - 1; diff --git a/src/som/vm/VmSettings.java b/src/som/vm/VmSettings.java index e855373995..840099c5cf 100644 --- a/src/som/vm/VmSettings.java +++ b/src/som/vm/VmSettings.java @@ -46,6 +46,10 @@ public class VmSettings implements Settings { public static final boolean SENDER_SIDE_REPLAY; public static final boolean RECEIVER_SIDE_REPLAY; + public static final boolean ACTOR_ASYNC_STACK_TRACE_STRUCTURE; + public static final boolean ACTOR_ASYNC_STACK_TRACE_METHOD_CACHE; + public static final boolean ACTOR_ASYNC_STACK_TRACE_INLINE_CACHE; + public static final String BASE_DIRECTORY; public static final boolean USE_PINNING; @@ -80,6 +84,12 @@ public class VmSettings implements Settings { SENDER_SIDE_REPLAY = REPLAY && !receiverSide; RECEIVER_SIDE_REPLAY = REPLAY && receiverSide; + ACTOR_ASYNC_STACK_TRACE_STRUCTURE = getBool("som.actorAsyncStackTraceStructure", false); + ACTOR_ASYNC_STACK_TRACE_METHOD_CACHE = + getBool("som.actorAsyncStackTraceMethodCache", false); + ACTOR_ASYNC_STACK_TRACE_INLINE_CACHE = + getBool("som.actorAsyncStackTraceInlineCache", false); + TEST_SNAPSHOTS = getBool("som.snapshotTest", false); TEST_SERIALIZE_ALL = getBool("som.actorSnapshotAll", false); SNAPSHOTS_ENABLED = getBool("som.actorSnapshot", false) || TEST_SNAPSHOTS; diff --git a/src/som/vmobjects/SBlock.java b/src/som/vmobjects/SBlock.java index 768f3273a0..6ddc8eba91 100644 --- a/src/som/vmobjects/SBlock.java +++ b/src/som/vmobjects/SBlock.java @@ -33,6 +33,7 @@ import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.frame.VirtualFrame; import som.VM; import som.interop.ValueConversion; @@ -95,7 +96,6 @@ public Object execute(final Object[] args) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { VM.thisMethodNeedsToBeOptimized( "Not ready for compilation, just moved from old interop code"); - if (TruffleOptions.AOT) { CompilerDirectives.transferToInterpreterAndInvalidate(); } @@ -107,7 +107,7 @@ public Object execute(final Object[] args) try { Object[] arguments = ValueConversion.convertToArgArray(convert, this, args); - Object result = block.executeDispatch(arguments); + Object result = block.executeDispatch(context, arguments); if (result == Nil.nilObject) { return null; diff --git a/src/som/vmobjects/SClass.java b/src/som/vmobjects/SClass.java index 787209c6ff..fa2e3c4b0d 100644 --- a/src/som/vmobjects/SClass.java +++ b/src/som/vmobjects/SClass.java @@ -328,8 +328,7 @@ public Dispatchable lookupPrivate(final SSymbol selector, */ public Dispatchable lookupMessage(final SSymbol selector, final AccessModifier hasAtLeast) { - assert hasAtLeast.ordinal() >= AccessModifier.PROTECTED.ordinal() - : "Access modifier should be protected or public"; + assert hasAtLeast.ordinal() >= AccessModifier.PROTECTED.ordinal() : "Access modifier should be protected or public"; VM.callerNeedsToBeOptimized("should never be called on fast path"); Dispatchable disp = dispatchables.get(selector); diff --git a/src/som/vmobjects/SFileDescriptor.java b/src/som/vmobjects/SFileDescriptor.java index 55d4d8d6e3..deac3de774 100644 --- a/src/som/vmobjects/SFileDescriptor.java +++ b/src/som/vmobjects/SFileDescriptor.java @@ -8,12 +8,15 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.BranchProfile; +import som.interpreter.SArguments; import som.interpreter.nodes.ExceptionSignalingNode; import som.interpreter.nodes.dispatch.BlockDispatchNode; import som.vm.NotYetImplementedException; import som.vm.Symbols; +import som.vm.VmSettings; import som.vm.constants.Classes; import som.vmobjects.SArray.SMutableArray; @@ -46,15 +49,16 @@ public SFileDescriptor(final String uri) { f = new File(uri); } - @TruffleBoundary - public Object openFile(final SBlock fail, final BlockDispatchNode dispatchHandler) { + // @TruffleBoundary + public Object openFile(final VirtualFrame frame, final SBlock fail, + final BlockDispatchNode dispatchHandler) { long[] storage = new long[bufferSize]; buffer = new SMutableArray(storage, Classes.arrayClass); try { raf = open(); } catch (FileNotFoundException e) { - return dispatchHandler.executeDispatch(new Object[] {fail, FILE_NOT_FOUND}); + return dispatchHandler.executeDispatch(frame, new Object[] {fail, FILE_NOT_FOUND}); } return this; @@ -66,7 +70,11 @@ private RandomAccessFile open() throws FileNotFoundException { } @TruffleBoundary - public void closeFile(final ExceptionSignalingNode ioException) { + private String getMessage(final IOException e) { + return e.getMessage(); + } + + public void closeFile(final VirtualFrame frame, final ExceptionSignalingNode ioException) { if (raf == null) { return; } @@ -74,7 +82,7 @@ public void closeFile(final ExceptionSignalingNode ioException) { try { closeFile(); } catch (IOException e) { - ioException.signal(e.getMessage()); + ioException.signal(frame, getMessage(e)); } } @@ -84,17 +92,25 @@ private void closeFile() throws IOException { raf = null; } - public int read(final long position, final SBlock fail, + public int read(final VirtualFrame frame, final long position, final SBlock fail, final BlockDispatchNode dispatchHandler, final BranchProfile errorCases) { + Object[] arguments; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + arguments = new Object[] {fail, null, SArguments.getShadowStackEntry(frame)}; + } else { + arguments = new Object[] {fail, null}; + } if (raf == null) { errorCases.enter(); - fail.getMethod().invoke(new Object[] {fail, FILE_IS_CLOSED}); + arguments[1] = FILE_IS_CLOSED; + fail.getMethod().invoke(arguments); return 0; } if (accessMode == AccessModes.write) { errorCases.enter(); - fail.getMethod().invoke(new Object[] {fail, WRITE_ONLY_MODE}); + arguments[1] = WRITE_ONLY_MODE; + fail.getMethod().invoke(arguments); return 0; } @@ -109,7 +125,8 @@ public int read(final long position, final SBlock fail, bytes = read(position, buff); } catch (IOException e) { errorCases.enter(); - dispatchHandler.executeDispatch(new Object[] {fail, toString(e)}); + arguments[2] = toString(e); + dispatchHandler.executeDispatch(frame, arguments); } // move read data to the storage @@ -133,12 +150,12 @@ private String toString(final IOException e) { return e.toString(); } - public void write(final int nBytes, final long position, final SBlock fail, - final BlockDispatchNode dispatchHandler, final ExceptionSignalingNode ioException, - final BranchProfile errorCases) { + public void write(final VirtualFrame frame, final int nBytes, final long position, + final SBlock fail, final BlockDispatchNode dispatchHandler, + final ExceptionSignalingNode ioException, final BranchProfile errorCases) { if (raf == null) { errorCases.enter(); - dispatchHandler.executeDispatch(new Object[] {fail, FILE_IS_CLOSED}); + dispatchHandler.executeDispatch(frame, new Object[] {fail, FILE_IS_CLOSED}); return; } @@ -155,7 +172,7 @@ public void write(final int nBytes, final long position, final SBlock fail, long val = storage[i]; if (val <= Byte.MIN_VALUE && Byte.MAX_VALUE <= val) { errorCases.enter(); - ioException.signal(errorMsg(val)); + ioException.signal(frame, errorMsg(val)); } buff[i] = (byte) val; } @@ -164,7 +181,7 @@ public void write(final int nBytes, final long position, final SBlock fail, write(nBytes, position, buff); } catch (IOException e) { errorCases.enter(); - dispatchHandler.executeDispatch(new Object[] {fail, toString(e)}); + dispatchHandler.executeDispatch(frame, new Object[] {fail, toString(e)}); } } @@ -180,12 +197,11 @@ private void write(final int nBytes, final long position, final byte[] buff) raf.write(buff, 0, nBytes); } - @TruffleBoundary - public long getFileSize(final ExceptionSignalingNode ioException) { + public long getFileSize(final VirtualFrame frame, final ExceptionSignalingNode ioException) { try { return length(); } catch (IOException e) { - ioException.signal(e.getMessage()); + ioException.signal(frame, getMessage(e)); } return 0; } diff --git a/src/som/vmobjects/SInvokable.java b/src/som/vmobjects/SInvokable.java index 7949edd4e5..d466a5dfe1 100644 --- a/src/som/vmobjects/SInvokable.java +++ b/src/som/vmobjects/SInvokable.java @@ -44,7 +44,7 @@ import som.interpreter.nodes.dispatch.CachedDispatchNode; import som.interpreter.nodes.dispatch.DispatchGuard; import som.interpreter.nodes.dispatch.Dispatchable; -import som.interpreter.nodes.dispatch.LexicallyBoundDispatchNode; +import som.interpreter.nodes.dispatch.LexicallyBoundDispatchNodeGen; import som.vm.Symbols; import som.vm.VmSettings; import som.vm.constants.Classes; @@ -191,7 +191,7 @@ public final AbstractDispatchNode getDispatchNode(final Object rcvr, // In case it's a private method, it is directly linked and doesn't need guards if (accessModifier == AccessModifier.PRIVATE) { - return new LexicallyBoundDispatchNode(next.getSourceSection(), ct); + return LexicallyBoundDispatchNodeGen.create(next.getSourceSection(), ct); } DispatchGuard guard = DispatchGuard.create(rcvr); diff --git a/src/tools/concurrency/KomposTrace.java b/src/tools/concurrency/KomposTrace.java index 4157b5bd3f..1cc63227e5 100644 --- a/src/tools/concurrency/KomposTrace.java +++ b/src/tools/concurrency/KomposTrace.java @@ -1,9 +1,15 @@ package tools.concurrency; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.source.SourceSection; import bd.source.SourceCoordinate; +import som.interpreter.Types; import som.interpreter.actors.Actor; import som.interpreter.nodes.dispatch.Dispatchable; import som.vm.Activity; @@ -16,6 +22,7 @@ import tools.debugger.entities.ActivityType; import tools.debugger.entities.DynamicScopeType; import tools.debugger.entities.Implementation; +import tools.debugger.entities.MessageReception; import tools.debugger.entities.PassiveEntityType; import tools.debugger.entities.ReceiveOp; import tools.debugger.entities.SendOp; @@ -23,12 +30,31 @@ public class KomposTrace { + private static Map> messagesReceivedByActor = new HashMap<>(); + + // private static Map> buffersByActor = new HashMap<>(); + // public static boolean missingBuffers(long actorSuspendedId) { + // List buffers = buffersByActor.get(actorSuspendedId); + // Collections.sort(buffers); + // + // for (int i = 1; i < buffers.size(); i++) { + // int previous = buffers.get(i - 1); + // int current = buffers.get(i); + // if (current != (previous + 1)) { + // return true; + // } + // } + // + // return false; + // } + public static void recordMainActor(final Actor mainActor, final ObjectSystem objectSystem) { KomposTraceBuffer buffer = KomposTraceBuffer.create(0); buffer.recordCurrentActivity(mainActor); buffer.recordMainActor(mainActor, objectSystem); - buffer.recordSendOperation(SendOp.ACTOR_MSG, 0, mainActor.getId(), mainActor); + buffer.recordSendOperation(SendOp.ACTOR_MSG, 0, mainActor.getId(), mainActor, (short) 0, 0, + null, null); buffer.returnBuffer(null); } @@ -82,9 +108,12 @@ public static void promiseResolution(final long promiseId, final Object value) { assert current instanceof TracingActivityThread; TracingActivityThread t = (TracingActivityThread) current; + String name = Types.toDebuggerString(value); + byte[] nameBytes = name.getBytes(); + ((KomposTraceBuffer) t.getBuffer()).recordSendOperation(SendOp.PROMISE_RESOLUTION, 0, promiseId, - t.getActivity()); + t.getActivity(), (short) 0, 0, null, nameBytes); t.resolvedPromises++; } @@ -92,9 +121,11 @@ public static void promiseError(final long promiseId, final Object value) { Thread current = Thread.currentThread(); assert current instanceof TracingActivityThread; TracingActivityThread t = (TracingActivityThread) current; + String error = Types.toDebuggerString(value); + byte[] errorBytes = error.getBytes(); ((KomposTraceBuffer) t.getBuffer()).recordSendOperation(SendOp.PROMISE_RESOLUTION, 0, promiseId, - t.getActivity()); + t.getActivity(), (short) 0, 0, null, errorBytes); t.erroredPromises++; } @@ -107,15 +138,17 @@ public static void promiseError(final long promiseId, final Object value) { public static void promiseChained(final long promiseValueId, final long promiseId) { TracingActivityThread t = getThread(); ((KomposTraceBuffer) t.getBuffer()).recordSendOperation( - SendOp.PROMISE_RESOLUTION, promiseValueId, promiseId, t.getActivity()); + SendOp.PROMISE_RESOLUTION, promiseValueId, promiseId, t.getActivity(), (short) 0, 0, + null, null); t.resolvedPromises++; } public static void sendOperation(final SendOp op, final long entityId, - final long targetId) { + final long targetId, final SSymbol selector, final long targetActorId, + final SourceSection msgSourceCoordinate) { TracingActivityThread t = getThread(); ((KomposTraceBuffer) t.getBuffer()).recordSendOperation(op, entityId, targetId, - t.getActivity()); + t.getActivity(), selector.getSymbolId(), targetActorId, msgSourceCoordinate, null); } public static void receiveOperation(final ReceiveOp op, final long sourceId) { @@ -136,6 +169,54 @@ private static TracingActivityThread getThread() { return (TracingActivityThread) current; } + public static void recordSuspendedActivityByDebugger(final TracingActivityThread t) { + ((KomposTraceBuffer) t.getBuffer()).recordPausedActivity(t.getActivity()); + } + + public static void actorMessageReception(final long messageId, + final TracingActivityThread t) { + // so.println("***** "+((Actor.ActorProcessingThread)t).getCurrentActor() +" + // activity "+((Actor.ActorProcessingThread)t).getActivity()); + Activity activity = t.getActivity(); + if (activity == null) { + activity = ((Actor.ActorProcessingThread) t).getCurrentActor(); + } + + if (!messageReceivedRecorded(messageId, activity.getId())) { + ((KomposTraceBuffer) t.getBuffer()).recordMessageReceived(MessageReception.MESSAGE_RCV, + activity, messageId); + } + } + + /** + * Check the message has not been saved before. + * Messages received can be repeated because we record them at the point where the message is + * sent and when the messages are processed. + * This is needed because at the point where the message is append in the mailbox the + * TracingActivityThread may not be available, + * they become available when the actor is about to process the messages. + * + * @param messageId + * @param actorId + * @return + */ + private static boolean messageReceivedRecorded(final long messageId, final long actorId) { + List messages; + if (!messagesReceivedByActor.containsKey(actorId)) { + messages = new ArrayList<>(); + } else { + messages = messagesReceivedByActor.get(actorId); + } + + if (messages.contains(messageId)) { + return true; + } else { // new message + messages.add(messageId); + messagesReceivedByActor.put(actorId, messages); + } + return false; + } + public static class KomposTraceBuffer extends TraceBuffer { /** @@ -175,6 +256,10 @@ boolean swapStorage(final Activity current) { return true; } + public void recordPausedActivity(final Activity current) { + recordCurrentActivity(current); + } + @TruffleBoundary protected boolean ensureSufficientSpace(final int requiredSpace, final Activity current) { if ((position + requiredSpace) >= VmSettings.BUFFER_SIZE) { @@ -216,9 +301,9 @@ private void recordThreadId() { assert position == start + Implementation.IMPL_THREAD.getSize(); } - public void recordCurrentActivity(final Activity current) { + public synchronized void recordCurrentActivity(final Activity current) { - if (current == lastActivity || current == null) { + if (current == null) { return; } @@ -229,8 +314,20 @@ public void recordCurrentActivity(final Activity current) { final int start = position; put(Implementation.IMPL_CURRENT_ACTIVITY.getId()); - putLong(current.getId()); - putInt(current.getNextTraceBufferId()); + long actorId = current.getId(); + int bufferId = current.getNextTraceBufferId(); + + putLong(actorId); + putInt(bufferId); + + // List bufferList; + // if (buffersByActor.containsKey(actorId)) { + // bufferList = buffersByActor.get(actorId); + // } else { + // bufferList = new ArrayList<>(); + // } + // bufferList.add(bufferId); + // buffersByActor.put(actorId, bufferList); assert position == start + Implementation.IMPL_CURRENT_ACTIVITY.getSize(); } @@ -248,9 +345,9 @@ private void writeSourceSection(final SourceSection origin) { return; } - assert !origin.getSource() - .isInternal() - : "Need special handling to ensure we see user code reported to trace/debugger"; + // assert !origin.getSource() + // .isInternal() : "Need special handling to ensure we see user code reported to + // trace/debugger"; putShort(Symbols.symbolFor(SourceCoordinate.getURI(origin.getSource())).getSymbolId()); putShort((short) origin.getStartLine()); putShort((short) origin.getStartColumn()); @@ -335,14 +432,55 @@ public void recordReceiveOperation(final ReceiveOp op, final long sourceId, } public void recordSendOperation(final SendOp op, final long entityId, - final long targetId, final Activity current) { - int requiredSpace = op.getSize(); + final long targetId, final Activity current, final short symbolId, + final long targetActorId, + final SourceSection msgSourceCoordinate, final byte[] value) { + int requiredSpace; + if (VmSettings.KOMPOS_TRACING) { + requiredSpace = op.getSize(value); + } else { + requiredSpace = op.getSize(); + } + ensureSufficientSpace(requiredSpace, current); final int start = position; put(op.getId()); putLong(entityId); putLong(targetId); + putLong(targetActorId); + putShort(symbolId); + + if (VmSettings.KOMPOS_TRACING) { + writeSourceSection(msgSourceCoordinate); + + if (value == null) { + putInt(0); + } else { + putInt(value.length); + for (byte b : value) { + put(b); + } + } + } + + assert position == start + requiredSpace; + } + + public void recordMessageReceived(final MessageReception mr, final Activity current, + final long messageId) { + int requiredSpace = mr.getSize(); + ensureSufficientSpace(requiredSpace, current); + + final int start = position; + put(mr.getId()); + putLong(messageId); + + // so.println("-message received "+messageId +" actor "+current.getId()); + + // if (position != start + requiredSpace) { + // so.println(); + // } assert position == start + requiredSpace; } @@ -392,10 +530,17 @@ public synchronized void recordReceiveOperation(final ReceiveOp op, @Override public synchronized void recordSendOperation(final SendOp op, - final long entityId, final long targetId, final Activity current) { - super.recordSendOperation(op, entityId, targetId, current); + final long entityId, final long targetId, final Activity current, final short symbol, + final long targetActorId, final SourceSection section, final byte[] value) { + super.recordSendOperation(op, entityId, targetId, current, symbol, targetActorId, + section, value); + } + + @Override + public synchronized void recordMessageReceived(final MessageReception mr, + final Activity current, final long messageId) { + super.recordMessageReceived(mr, current, messageId); } } } - } diff --git a/src/tools/concurrency/TraceBuffer.java b/src/tools/concurrency/TraceBuffer.java index 7fee9fdfc4..6173618a1b 100644 --- a/src/tools/concurrency/TraceBuffer.java +++ b/src/tools/concurrency/TraceBuffer.java @@ -15,8 +15,8 @@ public abstract class TraceBuffer { public static TraceBuffer create(final long threadId) { assert VmSettings.UNIFORM_TRACING || VmSettings.KOMPOS_TRACING; - if (VmSettings.KOMPOS_TRACING) { - return new KomposTrace.KomposTraceBuffer(threadId); + if (VmSettings.KOMPOS_TRACING || VmSettings.TRUFFLE_DEBUGGER_ENABLED) { + return new KomposTrace.KomposTraceBuffer.SyncedKomposTraceBuffer(threadId); } else { return new UniformTraceBuffer(); } diff --git a/src/tools/concurrency/TracingActors.java b/src/tools/concurrency/TracingActors.java index 50aaff306e..538054f178 100644 --- a/src/tools/concurrency/TracingActors.java +++ b/src/tools/concurrency/TracingActors.java @@ -1,6 +1,7 @@ package tools.concurrency; import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; @@ -19,6 +20,7 @@ import som.interpreter.actors.SPromise.STracingPromise; import som.vm.VmSettings; import tools.debugger.WebDebugger; +import tools.debugger.frontend.Suspension; import tools.replay.PassiveEntityWithEvents; import tools.replay.ReplayRecord; import tools.replay.TraceParser; @@ -40,6 +42,13 @@ public static class TracingActor extends Actor { */ protected boolean stepToNextTurn; + /** + * Saves all ids and the instances of the actors created in the system. + */ + private static Map allActors = new HashMap<>(); + + private static WebDebugger debugger; + public TracingActor(final VM vm) { super(vm); this.activityId = TracingActivityThread.newEntityId(); @@ -48,6 +57,9 @@ public TracingActor(final VM vm) { if (VmSettings.SNAPSHOTS_ENABLED) { snapshotRecord = new SnapshotRecord(); } + if (VmSettings.TRUFFLE_DEBUGGER_ENABLED) { + debugger = vm.getWebDebugger(); + } } protected TracingActor(final VM vm, final long id) { @@ -55,6 +67,16 @@ protected TracingActor(final VM vm, final long id) { this.activityId = id; } + public static void saveMessageReceived(final Actor actor, final EventualMessage message) { + TracingActivityThread tracingActivityThread = + TracingBackend.getTracingActivityThread(actor.getId()); + if (tracingActivityThread != null) { + // so.println("saveMessageReceived "+message.getMessageId() + " + // "+actor.getId()); + KomposTrace.actorMessageReception(message.getMessageId(), tracingActivityThread); + } + } + public final int getActorId() { // TODO: remove after rebasing snapshot PR throw new UnsupportedOperationException("Please remove this call and use getId instead"); @@ -142,6 +164,30 @@ public static void handleBreakpointsAndStepping(final EventualMessage msg, public DeserializationBuffer getDeserializationBuffer() { return null; } + + public static void saveActor(final Actor actor) { + allActors.put(actor.getId(), actor); + } + + public static Actor getActorById(final long actorId) { + return allActors.get(actorId); + } + + /** + * Stop actor execution if they are paused to avoid open threads before system shutdown. + */ + public static void stopActorsIfSuspended() { + for (long actorId : allActors.keySet()) { + if (actorId > 0) { // do not stop Platform actor + Suspension suspension = debugger.getSuspension(actorId); + if (suspension != null && suspension.getEvent() != null + && !suspension.getEvent().isDisposed()) { + suspension.getEvent().prepareKill(); + suspension.resume(); + } + } + } + } } public static final class ReplayActor extends TracingActor @@ -351,8 +397,7 @@ private Queue determineNextMessagesSenderSide( } assert toProcess.size() - + orderedMessages.size() == numReceivedMsgs - : "We shouldn't lose any messages here."; + + orderedMessages.size() == numReceivedMsgs : "We shouldn't lose any messages here."; return toProcess; } diff --git a/src/tools/concurrency/TracingBackend.java b/src/tools/concurrency/TracingBackend.java index 48f33564fe..7ea551022b 100644 --- a/src/tools/concurrency/TracingBackend.java +++ b/src/tools/concurrency/TracingBackend.java @@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.sun.management.GarbageCollectionNotificationInfo; +import som.interpreter.actors.Actor; import som.interpreter.actors.Actor.ActorProcessingThread; import som.vm.VmSettings; import som.vm.constants.Classes; @@ -247,6 +248,22 @@ public static void unregisterThread(final TracingActivityThread t) { } } + public static TracingActivityThread getTracingActivityThread(final long actorId) { + synchronized (tracingThreads) { + TracingActivityThread[] result = tracingThreads.toArray(new TracingActivityThread[0]); + for (TracingActivityThread tracingActivityThread : result) { + if ((tracingActivityThread instanceof ActorProcessingThread)) { + Actor currentActor = + ((ActorProcessingThread) tracingActivityThread).getCurrentActor(); + if (currentActor != null && currentActor.getId() == actorId) { + return tracingActivityThread; + } + } + } + } + return null; + } + public static final void forceSwapBuffers() { assert VmSettings.UNIFORM_TRACING || (VmSettings.TRUFFLE_DEBUGGER_ENABLED); @@ -283,6 +300,7 @@ public static final void forceSwapBuffers() { } else if (t.swapTracingBufferIfThreadSuspendedInDebugger()) { runningThreads -= 1; result[i] = null; + KomposTrace.recordSuspendedActivityByDebugger(t); } else if (isBlockedInJava(t)) { runningThreads -= 1; result[i] = null; diff --git a/src/tools/concurrency/TracingChannel.java b/src/tools/concurrency/TracingChannel.java index 2a0a21c0eb..b9b2a61285 100644 --- a/src/tools/concurrency/TracingChannel.java +++ b/src/tools/concurrency/TracingChannel.java @@ -3,12 +3,16 @@ import java.util.concurrent.SynchronousQueue; import som.interpreter.processes.SChannel; +import som.vm.Symbols; +import som.vmobjects.SSymbol; import tools.debugger.entities.ReceiveOp; import tools.debugger.entities.SendOp; import tools.replay.nodes.RecordEventNodes.RecordOneEvent; public final class TracingChannel extends SChannel { + private static final SSymbol writeSym = Symbols.symbolFor("write:"); + protected final long channelId; protected int messageId; @@ -55,7 +59,7 @@ public void write(final Object value, final RecordOneEvent traceWrite) super.write(value, traceWrite); } finally { KomposTrace.sendOperation( - SendOp.CHANNEL_SEND, current.messageId, current.channelId); + SendOp.CHANNEL_SEND, current.messageId, current.channelId, writeSym, 0, null); } } } diff --git a/src/tools/debugger/FrontendConnector.java b/src/tools/debugger/FrontendConnector.java index 98522f88fb..151342f093 100644 --- a/src/tools/debugger/FrontendConnector.java +++ b/src/tools/debugger/FrontendConnector.java @@ -24,6 +24,7 @@ import bd.source.SourceCoordinate; import bd.source.TaggedSourceCoordinate; +import som.interpreter.actors.Actor; import som.vm.VmSettings; import som.vmobjects.SSymbol; import tools.Tagging; @@ -31,11 +32,14 @@ import tools.concurrency.TracingBackend; import tools.debugger.WebSocketHandler.MessageHandler; import tools.debugger.WebSocketHandler.TraceHandler; +import tools.debugger.breakpoints.Breakpoints; +import tools.debugger.breakpoints.LineBreakpoint; import tools.debugger.entities.ActivityType; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.DynamicScopeType; import tools.debugger.entities.EntityType; import tools.debugger.entities.Implementation; +import tools.debugger.entities.MessageReception; import tools.debugger.entities.PassiveEntityType; import tools.debugger.entities.ReceiveOp; import tools.debugger.entities.SendOp; @@ -44,7 +48,9 @@ import tools.debugger.message.InitializationResponse; import tools.debugger.message.Message; import tools.debugger.message.Message.OutgoingMessage; +import tools.debugger.message.PauseActorResponse; import tools.debugger.message.ProgramInfoResponse; +import tools.debugger.message.ResumeActorResponse; import tools.debugger.message.ScopesResponse; import tools.debugger.message.SourceMessage; import tools.debugger.message.SourceMessage.SourceData; @@ -53,8 +59,6 @@ import tools.debugger.message.SymbolMessage; import tools.debugger.message.VariablesRequest.FilterType; import tools.debugger.message.VariablesResponse; -import tools.debugger.session.Breakpoints; -import tools.debugger.session.LineBreakpoint; /** @@ -90,6 +94,8 @@ public class FrontendConnector { */ private CompletableFuture clientConnected; + private CompletableFuture messageSocketInitialized; + private final Gson gson; private static final int MESSAGE_PORT = 7977; private static final int TRACE_PORT = 7978; @@ -107,6 +113,7 @@ public FrontendConnector(final Breakpoints breakpoints, this.gson = gson; clientConnected = new CompletableFuture(); + messageSocketInitialized = new CompletableFuture(); try { log("[DEBUGGER] Initialize HTTP and WebSocket Server for Debugger"); @@ -254,10 +261,6 @@ public void sendSymbols(final ArrayList symbolsToWrite) { send(new SymbolMessage(symbolsToWrite)); } - public void sendTracingData(final ByteBuffer buffer) { - traceSocket.send(buffer); - } - public void awaitClient() { assert VmSettings.TRUFFLE_DEBUGGER_ENABLED; assert clientConnected != null; @@ -268,6 +271,7 @@ public void awaitClient() { try { messageSocket = clientConnected.get(); assert messageSocket != null; + messageSocketInitialized.complete(messageSocket); traceSocket = traceHandler.getConnection().get(); assert traceSocket != null; @@ -304,8 +308,23 @@ public void sendTracingData() { } } + public void sendTracingData(final ByteBuffer buffer) { + // log("[DEBUGGER] Trace buffers sent: "+buffer); + traceSocket.send(buffer); + } + public void sendProgramInfo() { - send(ProgramInfoResponse.create(webDebugger.vm.getArguments())); + // when the server has really started, i.e. the client has connected, then do the send + messageSocketInitialized.thenRun( + () -> send(ProgramInfoResponse.create(webDebugger.vm.getArguments()))); + } + + public void sendPauseActorResponse(final long pausedActorId) { + send(PauseActorResponse.create(pausedActorId)); + } + + public void sendResumeActorResponse(final long actorId) { + send(ResumeActorResponse.create(actorId)); } public void registerOrUpdate(final LineBreakpoint bp) { @@ -320,7 +339,11 @@ public Suspension getSuspensionForGlobalId(final long globalId) { return webDebugger.getSuspension(TraceData.getActivityIdFromGlobalValId(globalId)); } - static void log(final String str) { + public Actor getActorById(final long activityId) { + return webDebugger.getActorById(activityId); + } + + public static void log(final String str) { // Checkstyle: stop System.out.println(str); // Checkstyle: resume @@ -330,10 +353,19 @@ public void completeConnection(final WebSocket conn) { Runtime.getRuntime().addShutdownHook(new Thread(() -> closeAllSockets())); clientConnected.complete(conn); + + // when the server has really started, i.e. the client has connected, then do the send + messageSocketInitialized.thenRun(() -> sendInitResponse()); + } + + private void sendInitResponse() { + // log("[DEBUGGER] Message socket initialized "+messageSocketInitialized.isDone()); + send(InitializationResponse.create(EntityType.values(), ActivityType.values(), PassiveEntityType.values(), DynamicScopeType.values(), SendOp.values(), ReceiveOp.values(), - BreakpointType.values(), SteppingType.values(), Implementation.values())); + BreakpointType.values(), SteppingType.values(), Implementation.values(), + MessageReception.values())); } private void closeAllSockets() { diff --git a/src/tools/debugger/RuntimeReflectionRegistration.java b/src/tools/debugger/RuntimeReflectionRegistration.java index d32b04da11..a6a1723d01 100644 --- a/src/tools/debugger/RuntimeReflectionRegistration.java +++ b/src/tools/debugger/RuntimeReflectionRegistration.java @@ -13,27 +13,12 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import tools.debugger.message.InitializationResponse; -import tools.debugger.message.InitializeConnection; +import tools.debugger.breakpoints.BreakpointInfo; +import tools.debugger.breakpoints.LineBreakpoint; +import tools.debugger.breakpoints.SectionBreakpoint; +import tools.debugger.message.*; import tools.debugger.message.Message.IncommingMessage; import tools.debugger.message.Message.OutgoingMessage; -import tools.debugger.message.ProgramInfoRequest; -import tools.debugger.message.ProgramInfoResponse; -import tools.debugger.message.ScopesRequest; -import tools.debugger.message.ScopesResponse; -import tools.debugger.message.SourceMessage; -import tools.debugger.message.StackTraceRequest; -import tools.debugger.message.StackTraceResponse; -import tools.debugger.message.StepMessage; -import tools.debugger.message.StoppedMessage; -import tools.debugger.message.SymbolMessage; -import tools.debugger.message.TraceDataRequest; -import tools.debugger.message.UpdateBreakpoint; -import tools.debugger.message.VariablesRequest; -import tools.debugger.message.VariablesResponse; -import tools.debugger.session.BreakpointInfo; -import tools.debugger.session.LineBreakpoint; -import tools.debugger.session.SectionBreakpoint; /** @@ -79,6 +64,8 @@ public void register(final String name, final Class klass) { outMsgs.register(ScopesResponse.class); outMsgs.register(VariablesResponse.class); outMsgs.register(ProgramInfoResponse.class); + outMsgs.register("pauseActorResponse", PauseActorResponse.class); + outMsgs.register("resumeActorResponse", ResumeActorResponse.class); ClassGroup inMsgs = new ClassGroup(IncommingMessage.class, "action", true); inMsgs.register(InitializeConnection.class); @@ -89,6 +76,8 @@ public void register(final String name, final Class klass) { inMsgs.register(VariablesRequest.class); inMsgs.register(ProgramInfoRequest.class); inMsgs.register(TraceDataRequest.class); + inMsgs.register(EvaluateExpressionRequest.class); + inMsgs.register("pauseActorMessageReceiver", PauseActorRequest.class); ClassGroup bps = new ClassGroup(BreakpointInfo.class, "type", true); bps.register(LineBreakpoint.class); diff --git a/src/tools/debugger/Tags.java b/src/tools/debugger/Tags.java index 69fd083e85..82dcedf6aa 100644 --- a/src/tools/debugger/Tags.java +++ b/src/tools/debugger/Tags.java @@ -24,6 +24,8 @@ import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.source.SourceSection; +import som.VM; + public abstract class Tags { private Tags() {} diff --git a/src/tools/debugger/WebDebugger.java b/src/tools/debugger/WebDebugger.java index 593e19bf11..4670c45ecc 100644 --- a/src/tools/debugger/WebDebugger.java +++ b/src/tools/debugger/WebDebugger.java @@ -25,12 +25,14 @@ import bd.source.SourceCoordinate; import som.VM; +import som.interpreter.actors.Actor; import som.vm.Activity; import som.vm.Symbols; import tools.TraceData; import tools.concurrency.TracingActivityThread; +import tools.concurrency.TracingActors; +import tools.debugger.breakpoints.Breakpoints; import tools.debugger.frontend.Suspension; -import tools.debugger.session.Breakpoints; /** @@ -108,7 +110,7 @@ public void prepareSteppingAfterNextRootNode(final Thread thread) { breakpoints.prepareSteppingAfterNextRootNode(thread); } - Suspension getSuspension(final long activityId) { + public Suspension getSuspension(final long activityId) { return idToSuspension.get(activityId); } @@ -178,5 +180,9 @@ public Breakpoints getBreakpoints() { return breakpoints; } + public Actor getActorById(long actorId) { + return TracingActors.TracingActor.getActorById(actorId); + } + private static Gson jsonProcessor = RuntimeReflectionRegistration.createJsonProcessor(); } diff --git a/src/tools/debugger/WebSocketHandler.java b/src/tools/debugger/WebSocketHandler.java index fdce3d7542..d0955ab815 100644 --- a/src/tools/debugger/WebSocketHandler.java +++ b/src/tools/debugger/WebSocketHandler.java @@ -34,7 +34,8 @@ public int awaitStartup() throws ExecutionException { while (true) { try { return connectionPort.get(); - } catch (InterruptedException e) { /* Retry on interrupt. */ } + } catch (InterruptedException e) { + /* Retry on interrupt. */ } } } @@ -64,6 +65,8 @@ public static class MessageHandler extends WebSocketHandler { public MessageHandler(final int port, final FrontendConnector connector, final Gson gson) { super(port); + this.setReuseAddr(true); + // this.setConnectionLostTimeout(0); // uncomment if timeout fails this.connector = connector; this.gson = gson; } @@ -86,6 +89,8 @@ public static class TraceHandler extends WebSocketHandler { public TraceHandler(final int port) { super(port); + this.setReuseAddr(true); + // this.setConnectionLostTimeout(0); // uncomment if timeout fails connection = new CompletableFuture<>(); } diff --git a/src/tools/debugger/asyncstacktraces/ShadowStackEntry.java b/src/tools/debugger/asyncstacktraces/ShadowStackEntry.java new file mode 100644 index 0000000000..d06c40711d --- /dev/null +++ b/src/tools/debugger/asyncstacktraces/ShadowStackEntry.java @@ -0,0 +1,170 @@ +package tools.debugger.asyncstacktraces; + +import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; + +import som.interpreter.SArguments; +import som.interpreter.actors.Actor.ActorProcessingThread; +import som.interpreter.actors.EventualMessage; +import som.interpreter.actors.SPromise; +import som.vm.VmSettings; + + +public class ShadowStackEntry { + + protected ShadowStackEntry previous; + protected final Node expression; + protected final long actorId; + + public static long numberOfAllocations; + + public static final boolean ALLOCATION_COUNT = false; + + public Node getExpression() { + return expression; + } + + public ShadowStackEntry getPreviousShadowStackEntry() { + return previous; + } + + public static ShadowStackEntry createTop(final Node expr) { + return new ShadowStackEntry(null, expr); + } + + public static ShadowStackEntry create(final ShadowStackEntry previous, + final Node expr) { + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || previous != null; + return new ShadowStackEntry(previous, unwrapNodeIfNecessary(expr)); + } + + public static ShadowStackEntry createAtAsyncSend(final ShadowStackEntry previous, + final Node expr) { + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || previous != null; + return new EntryAtMessageSend(previous, unwrapNodeIfNecessary(expr)); + } + + public static ShadowStackEntry createAtPromiseResolution(final ShadowStackEntry previous, + final Node expr, final EntryForPromiseResolution.ResolutionLocation resolutionType, + final String resolutionValue, SPromise promiseOrNull) { + assert !VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE || previous != null; + return new EntryForPromiseResolution(previous, unwrapNodeIfNecessary(expr), resolutionType, + resolutionValue, promiseOrNull); + } + + public static ShadowStackEntry createAtPromiseResolution(final ShadowStackEntry previous, + final Node expr, final EntryForPromiseResolution.ResolutionLocation resolutionType, + final String resolutionValue){ + return ShadowStackEntry.createAtPromiseResolution(previous, expr, resolutionType,resolutionValue,null); + } + + public static EntryForTaskSpawn createAtTaskSpawn(final ShadowStackEntry previous, final Node currentExpression){ + ShadowStackEntry methodCallEntry = new ShadowStackEntry(previous,currentExpression); + return new EntryForTaskSpawn(methodCallEntry,currentExpression); + } + + public static Node unwrapNodeIfNecessary(final Node node) { + if (node instanceof WrapperNode) { + return ((WrapperNode) node).getDelegateNode(); + } else { + return node; + } + } + + public ShadowStackEntry(final ShadowStackEntry previous, final Node expr) { + assert VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE; + this.previous = previous; + this.expression = expr; + if (ALLOCATION_COUNT) { + numberOfAllocations++; + } + //this.actorId = getCurrentActor(); + this.actorId = 0; + } + + public long getCurrentActor() { + Thread t = Thread.currentThread(); + if (t instanceof ActorProcessingThread) { + return EventualMessage.getActorCurrentMessageIsExecutionOn().getId(); + } else { + return -1L; + } + } + + public RootNode getRootNode() { + return expression.getRootNode(); + } + + public SourceSection getSourceSection() { + return expression.getSourceSection(); + } + + public boolean isAsync() { + return false; + } + + public void setPreviousShadowStackEntry(final ShadowStackEntry maybeEntry) { + previous = maybeEntry; + } + + public static final class EntryAtMessageSend extends ShadowStackEntry { + + private EntryAtMessageSend(final ShadowStackEntry previous, final Node expr) { + super(previous, expr); + } + } + + public static final class EntryForPromiseResolution extends ShadowStackEntry { + public enum ResolutionLocation { + SUCCESSFUL("resolved with a value"), ERROR("resolved with an error"), + CHAINED("resolved with a promise"), ON_WHEN_RESOLVED_BLOCK("on whenResolved block"), + ON_WHEN_RESOLVED("on whenResolved"), ON_WHEN_RESOLVED_ERROR("on whenResolved error"), + ON_RECEIVE_MESSAGE("on receive message"), ON_SCHEDULE_PROMISE("on schedule"); + + private final String label; + + ResolutionLocation(final String label) { + this.label = label; + } + + public String getValue() { + return label; + } + } + + public ResolutionLocation resolutionLocation; + public String resolutionValue; + public SPromise promiseGroupOrNull = null; + + private EntryForPromiseResolution(final ShadowStackEntry previous, + final Node expr, final ResolutionLocation resolutionLocation, + final String resolutionValue) { + super(previous, expr); + this.resolutionLocation = resolutionLocation; + this.resolutionValue = resolutionValue; + } + + private EntryForPromiseResolution(final ShadowStackEntry previous, + final Node expr, final ResolutionLocation resolutionLocation, + final String resolutionValue, SPromise promiseGroup) { + this(previous,expr,resolutionLocation,resolutionValue); + this.promiseGroupOrNull = promiseGroup; + } + + public boolean isPromiseGroup() { return promiseGroupOrNull != null; } + + @Override + public boolean isAsync() { + return true; + } + + } + public static final class EntryForTaskSpawn extends ShadowStackEntry { + + public EntryForTaskSpawn(ShadowStackEntry previous, Node expr) { + super(previous, expr); + } + } +} diff --git a/src/tools/debugger/asyncstacktraces/ShadowStackEntryLoad.java b/src/tools/debugger/asyncstacktraces/ShadowStackEntryLoad.java new file mode 100644 index 0000000000..f62974e1d9 --- /dev/null +++ b/src/tools/debugger/asyncstacktraces/ShadowStackEntryLoad.java @@ -0,0 +1,136 @@ +package tools.debugger.asyncstacktraces; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; + +import som.interpreter.SArguments; +import som.vm.VmSettings; + + +public abstract class ShadowStackEntryLoad extends Node { + public static final int NUM_SHADOW_STACK_ENTRIES = 6; + + public static final boolean ANALYSIS = false; + public static int cacheHit = 0; + public static int megaCacheHit = 0; + public static int megaMiss = 0; + + @CompilerDirectives.TruffleBoundary + public static ShadowStackEntryLoad create() { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + return new UninitializedShadowStackEntryLoad(); + } else { + return null; + } + } + + public void loadShadowStackEntry(final Object[] arguments, final Node expression, + final VirtualFrame frame, final boolean async) { + ShadowStackEntry prevEntry = SArguments.getShadowStackEntry(frame); + loadShadowStackEntry(arguments, expression, prevEntry, this, async); + } + + protected abstract void loadShadowStackEntry(Object[] arguments, + Node expression, + ShadowStackEntry prevEntry, + ShadowStackEntryLoad firstShadowStackEntryLoad, + boolean async); + + public abstract int getCurrentCacheSize(); + + protected void setShadowStackEntry(final ShadowStackEntry shadowStackEntry, + final Object[] arguments) { + SArguments.setShadowStackEntry(arguments, shadowStackEntry); + } + + private static final class UninitializedShadowStackEntryLoad extends ShadowStackEntryLoad { + + @CompilerDirectives.TruffleBoundary + @Override + protected void loadShadowStackEntry(final Object[] arguments, + final Node expression, + final ShadowStackEntry prevEntry, + final ShadowStackEntryLoad firstShadowStackEntryLoad, + final boolean async) { + if (!VmSettings.ACTOR_ASYNC_STACK_TRACE_INLINE_CACHE) { + setShadowStackEntry( + SArguments.instantiateShadowStackEntry(prevEntry, expression, async), + arguments); + return; + } + ShadowStackEntry newEntry = + SArguments.instantiateShadowStackEntry(prevEntry, expression, async); + ShadowStackEntryLoad newLoad; + if (firstShadowStackEntryLoad.getCurrentCacheSize() > NUM_SHADOW_STACK_ENTRIES) { + newLoad = new GenericShadowStackEntryLoad(); + // firstShadowStackEntryLoad.replace(newLoad); + replace(newLoad); + } else { + newLoad = new CachedShadowStackEntryLoad(prevEntry, newEntry); + replace(newLoad); + } + newLoad.loadShadowStackEntry(arguments, expression, prevEntry, + firstShadowStackEntryLoad, async); + } + + @Override + public int getCurrentCacheSize() { + return 0; + } + + } + + private static final class CachedShadowStackEntryLoad extends ShadowStackEntryLoad { + + @Child protected ShadowStackEntryLoad nextInCache; + protected final ShadowStackEntry expectedShadowStackEntry; + protected final ShadowStackEntry cachedShadowStackEntry; + + @CompilerDirectives.TruffleBoundary + CachedShadowStackEntryLoad(final ShadowStackEntry prevEntry, + final ShadowStackEntry newEntry) { + this.expectedShadowStackEntry = prevEntry; + this.cachedShadowStackEntry = newEntry; + nextInCache = new UninitializedShadowStackEntryLoad(); + } + + @Override + public int getCurrentCacheSize() { + return 1 + nextInCache.getCurrentCacheSize(); + } + + @Override + protected void loadShadowStackEntry(final Object[] arguments, + final Node expression, + final ShadowStackEntry prevEntry, final ShadowStackEntryLoad firstShadowStackEntryLoad, + final boolean async) { + if (prevEntry == expectedShadowStackEntry) { + setShadowStackEntry(cachedShadowStackEntry, arguments); + if (ANALYSIS) { + cacheHit++; + } + } else { + nextInCache.loadShadowStackEntry(arguments, expression, prevEntry, + firstShadowStackEntryLoad, async); + } + } + } + + private static final class GenericShadowStackEntryLoad extends ShadowStackEntryLoad { + + @Override + protected void loadShadowStackEntry(final Object[] arguments, + final Node expression, + final ShadowStackEntry prevEntry, final ShadowStackEntryLoad firstShadowStackEntryLoad, + final boolean async) { + setShadowStackEntry(SArguments.instantiateShadowStackEntry(prevEntry, expression, async), + arguments); + } + + @Override + public int getCurrentCacheSize() { + return 0; + } + } +} diff --git a/src/tools/debugger/asyncstacktraces/StackIterator.java b/src/tools/debugger/asyncstacktraces/StackIterator.java new file mode 100644 index 0000000000..8f8059bd93 --- /dev/null +++ b/src/tools/debugger/asyncstacktraces/StackIterator.java @@ -0,0 +1,501 @@ +package tools.debugger.asyncstacktraces; + +import java.util.*; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.debug.DebugStackFrame; +import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; +import com.oracle.truffle.api.frame.FrameInstanceVisitor; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; + +import som.interpreter.Invokable; +import som.interpreter.Method; +import som.interpreter.Primitive; +import som.interpreter.actors.EventualSendNode; +import som.interpreter.actors.SPromise; +import som.interpreter.nodes.dispatch.BackCacheCallNode; +import som.vm.VmSettings; +import tools.debugger.asyncstacktraces.ShadowStackEntry.EntryAtMessageSend; +import tools.debugger.asyncstacktraces.ShadowStackEntry.EntryForPromiseResolution; +import tools.debugger.asyncstacktraces.StackIterator.ShadowStackIterator.HaltShadowStackIterator; +import tools.debugger.asyncstacktraces.StackIterator.ShadowStackIterator.SuspensionShadowStackIterator; +import tools.debugger.frontend.ApplicationThreadStack; +import tools.debugger.frontend.ApplicationThreadStack.StackFrame; + + +/** + * This iterator traverses the run time stack and all available calling context + * information. + * + *

+ * In special cases, a single stack frame/calling context might be + * represented as multiple stack frames in the iteration. + * We chose to do this to explicitly represent context switches in asynchronous + * control flow, as caused for instance by eventual message sends or promise resolution. + */ + +public abstract class StackIterator implements Iterator { + + @Override + public abstract boolean hasNext(); + + @Override + public abstract StackFrame next(); + + protected StackFrame createStackFrame(final Frame localFrame, final RootNode rootNode, + final String name, final SourceSection location) { + return new StackFrame(name, rootNode, + location, localFrame, false); + } + + protected StackFrame createStackFrame(final Frame localFrame, final RootNode rootNode, + final String name, final SourceSection location, final boolean isFromMCOpt) { + return new StackFrame(name, rootNode, + location, localFrame, false,isFromMCOpt); + } + + public static StackIterator createHaltIterator(final SourceSection topNode) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + return new HaltShadowStackIterator(topNode); + } else { + return new HaltIterator(topNode); + } + } + + public static StackIterator createSuspensionIterator( + final Iterator localStack, final long actorId) { + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + return new SuspensionShadowStackIterator(localStack, actorId); + } else { + return new SuspensionIterator(localStack); + } + } + + public static class SuspensionIterator extends StackIterator { + + protected ArrayList frames; + protected int currentIndex = 0; + + public SuspensionIterator(final Iterator localStack) { + assert localStack != null; + frames = new ArrayList(); + while (localStack.hasNext()) { + frames.add(localStack.next()); + } + } + + @Override + public boolean hasNext() { + return currentIndex != frames.size(); + } + + @Override + public StackFrame next() { + DebugStackFrame next = frames.get(currentIndex); + currentIndex++; + return createStackFrame(next.getFrame(), + next.getRootNode(), + next.getRootNode().getName(), + next.getSourceSection()); + } + } + + public static class HaltIterator extends StackIterator { + + protected ArrayList stackFrames; + + protected int currentIndex = 0; + + public HaltIterator(final SourceSection topNode) { + stackFrames = new ArrayList(); + boolean[] isFirst = new boolean[] {true}; + + Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { + @Override + public Object visitFrame(final FrameInstance frameInstance) { + RootCallTarget rt = (RootCallTarget) frameInstance.getCallTarget(); + if (!(rt.getRootNode() instanceof Invokable)) { + return null; + } + + Invokable rootNode = (Invokable) rt.getRootNode(); + SourceSection ss = null; + if (frameInstance.getCallNode() != null) { + ss = frameInstance.getCallNode().getEncapsulatingSourceSection(); + } + + if (isFirst[0]) { + // for the first frame, we got the topNode's source section handed in + assert ss == null; + ss = topNode; + isFirst[0] = false; + } + + stackFrames.add( + createStackFrame( + frameInstance.getFrame(FrameAccess.READ_ONLY), + rootNode, rootNode.getName(), ss)); + return null; + } + }); + } + + @Override + public boolean hasNext() { + return currentIndex != stackFrames.size(); + } + + @Override + public StackFrame next() { + currentIndex += 1; + return stackFrames.get(currentIndex - 1); + } + } + + /** + * @author clementbera + *

+ * By contrast to HaltIterator and Suspension Iterator, the ShadowStackIterator use + * the stack for the first frame only and then rely on shadow stack entry to get the + * following stack entries + */ + public abstract static class ShadowStackIterator extends StackIterator { + private ShadowStackEntry currentSSEntry; + protected boolean first; + private Node currentNode; + public Invokable currentMethod; + private ShadowStackEntry useAgainShadowEntry; + private Frame useAgainFrame; + private StackFrame topFrame; + + public ShadowStackIterator() { + currentSSEntry = null; + first = true; + topFrame = null; + } + + @Override + public boolean hasNext() { + return currentSSEntry != null || first || useAgainShadowEntry != null; + } + + protected abstract StackFrameDescription getFirstFrame(); + + protected abstract StackFrameDescription getFrameBySource(SourceSection sourceSection); + + @Override + public StackFrame next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_METHOD_CACHE) { + return nextAsyncStackStructureMethodCache(); + } else { + return nextAsyncStackStructure(); + } + } + + public StackFrame nextAsyncStackStructure() { + ShadowStackEntry shadow = null; + Frame localFrame = null; + boolean usedAgain = false; + + if (first) { + StackFrameDescription firstFrame = getFirstFrame(); + localFrame = firstFrame.getFrame(); + Object[] args = localFrame.getArguments(); + // assert args[args.length - 1] instanceof ShadowStackEntry; + if (args[args.length - 1] instanceof ShadowStackEntry) { + currentSSEntry = (ShadowStackEntry) args[args.length - 1]; + } + + first = false; + + } else if (useAgainShadowEntry != null) { + shadow = useAgainShadowEntry; + usedAgain = true; + localFrame = useAgainFrame; + useAgainShadowEntry = null; + useAgainFrame = null; + } else { + shadow = currentSSEntry; + if (shadow != null) { + currentSSEntry = shadow.previous; + } + } + + return createStackFrame(shadow, localFrame, usedAgain); + + } + + public StackFrame getTopFrame() { + if (first) { + StackFrameDescription frameDescription = getFirstFrame(); + String actor = "actor " + frameDescription.getActorId() + ", "; + String name; + if (frameDescription.getRootNode().getName() != null) { // case of ExecutorRootNode + String rootNodeName = frameDescription.getRootNode().getName(); + name = actor.concat(rootNodeName); + } else { + name = actor; + } + topFrame = new StackFrame(name, frameDescription.getRootNode(), + frameDescription.getSourceSection(), frameDescription.getFrame(), false); + + } + return topFrame; + } + + protected StackFrame createStackFrame(final ShadowStackEntry shadow, + Frame localFrame, final boolean usedAgain) { + if (shadow == null) { + return null; + } + if (shadow.expression == null) { + return null; + } + boolean contextTransitionElement; + + String name = "actor " + shadow.actorId + ", " + shadow.getRootNode().getName(); + SourceSection location = shadow.getSourceSection(); + if (shadow instanceof ShadowStackEntry.EntryForTaskSpawn){ + name = "Task>>#spawnPrim:"; + } + + if (!usedAgain) { + contextTransitionElement = true; + + if (shadow instanceof ShadowStackEntry.EntryForTaskSpawn){ + name = "actor " + shadow.actorId + ", Task>>#spawnPrim:"; + } + + if (shadow instanceof EntryAtMessageSend) { + Node sendNode = shadow.expression.getParent(); + String symbol = ((EventualSendNode) sendNode).getSentSymbol().getString(); + + if (sendNode instanceof EventualSendNode) { + name = "actor " + shadow.actorId + ", on async message send: " + symbol; + + } + useAgainShadowEntry = shadow; + useAgainFrame = localFrame; + } else if (shadow instanceof EntryForPromiseResolution) { + + //below code is of carmen + EntryForPromiseResolution.ResolutionLocation resolutionLocation = + ((EntryForPromiseResolution) shadow).resolutionLocation; + String resolutionValue = ((EntryForPromiseResolution) shadow).resolutionValue; + name = "actor " + shadow.actorId + ", " + resolutionLocation.getValue() + ": " + + shadow.expression.getRootNode().getName() + " " + resolutionValue; + + if (((EntryForPromiseResolution) shadow).promiseGroupOrNull != null){ + SPromise promiseGroup = ((EntryForPromiseResolution) shadow).promiseGroupOrNull; + List asyncStacks = promiseGroup.getAsyncStacks(); + List> listOfStacks = new LinkedList<>(); + for (ShadowStackEntry entry : asyncStacks) { + //ShadowStackIterator iterator = new ShadowStackIterator.HaltShadowStackIterator(entry.getSourceSection()); + List internalList = new LinkedList<>(); + ShadowStackEntry current = entry; + while(current != null){ + if(current.expression != null) { + internalList.add(new StackFrame(current.getRootNode().getName(), current.getRootNode(), current.getSourceSection(), localFrame, false)); + } + current = current.previous; + } + listOfStacks.add(internalList); + } + return new ApplicationThreadStack.ParallelStack(listOfStacks); + } + } + + } else { + StackFrameDescription frameByName = getFrameBySource(location); + if (frameByName != null && localFrame == null) { + localFrame = frameByName.frame; + } + contextTransitionElement = false; + } + + return new StackFrame(name, shadow.getRootNode(), + location, localFrame, contextTransitionElement); + } + + // This version has to skip missing shadow stack entry using method back pointer cache + protected StackFrame nextAsyncStackStructureMethodCache() { + ShadowStackEntry shadow = null; + Frame localFrame = null; + boolean usedAgain = false; + + if (first) { + localFrame = getFirstFrame().getFrame(); + Object[] args = localFrame.getArguments(); + assert args[args.length - 1] instanceof ShadowStackEntry; + currentSSEntry = (ShadowStackEntry) args[args.length - 1]; + currentMethod = (Invokable) getFirstFrame().getRootNode(); + first = false; + } + + if (useAgainShadowEntry != null) { + shadow = useAgainShadowEntry; + usedAgain = true; + localFrame = useAgainFrame; + useAgainShadowEntry = null; + useAgainFrame = null; + } else { + if (shouldUsePreviousShadowStackEntry(currentMethod, + currentSSEntry.getExpression())) { + shadow = currentSSEntry; + currentSSEntry = currentSSEntry.getPreviousShadowStackEntry(); + // null if start frame + if (currentSSEntry != null) { + currentNode = currentSSEntry.getExpression(); + } + } else { + assert currentMethod instanceof Method; + currentNode = (Node) ((Method) currentMethod).getUniqueCaller(); + } + } + + if (shadow != null) { + return createStackFrame(shadow, localFrame, usedAgain); + } else { + return createStackFrame(localFrame, currentNode.getRootNode(), + currentNode.getRootNode().getName(), currentNode.getSourceSection(), true); + } + } + + public boolean shouldUsePreviousShadowStackEntry(final Invokable currentMethod, + final Node prevExpression) { + if (!(currentMethod instanceof Method)) { + return true; + } + if (prevExpression instanceof BackCacheCallNode) { + BackCacheCallNode ssNode = + (BackCacheCallNode) prevExpression; + return currentMethod == ssNode.getCachedMethod(); + } + return true; + } + + protected static final class StackFrameDescription { + SourceSection sourceSection; + Frame frame; + RootNode rootNode; + long actorId; + + public StackFrameDescription(final SourceSection sourceSection, + final Frame frame, final RootNode rootNode, final long actorId) { + this.sourceSection = sourceSection; + this.frame = frame; + this.rootNode = rootNode; + this.actorId = actorId; + } + + public SourceSection getSourceSection() { + return sourceSection; + } + + public Frame getFrame() { + return frame; + } + + public RootNode getRootNode() { + return rootNode; + } + + public long getActorId() { + return actorId; + } + } + + public static final class SuspensionShadowStackIterator extends ShadowStackIterator { + private final DebugStackFrame firstDebugFrame; + private StackFrameDescription firstFrameDescription; + private long actorId; + private List allFrames; + + public SuspensionShadowStackIterator(final Iterator localStack, + final long actorId) { + assert localStack != null; + assert localStack.hasNext(); + firstDebugFrame = localStack.next(); // first frame + firstFrameDescription = null; + this.actorId = actorId; + + allFrames = new ArrayList(); + while (localStack.hasNext()) { + allFrames.add(localStack.next()); // save all frames + } + } + + @Override + protected StackFrameDescription getFirstFrame() { + assert first; + if (firstFrameDescription == null) { + firstFrameDescription = new StackFrameDescription(firstDebugFrame.getSourceSection(), + firstDebugFrame.getFrame(), + firstDebugFrame.getRootNode(), this.actorId); + } + return firstFrameDescription; + } + + @Override + public StackFrameDescription getFrameBySource(final SourceSection sourceSection) { + for (DebugStackFrame debugStackFrame : allFrames) { + if (debugStackFrame.getSourceSection() != null + && debugStackFrame.getSourceSection().equals(sourceSection)) { + return new StackFrameDescription(debugStackFrame.getSourceSection(), + debugStackFrame.getFrame(), + debugStackFrame.getRootNode(), this.actorId); + } + } + + return null; + } + } + + public static final class HaltShadowStackIterator extends ShadowStackIterator { + private final StackFrameDescription firstFrame; + + public HaltShadowStackIterator(final SourceSection topNode) { + StackFrameDescription[] result = new StackFrameDescription[1]; + Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { + @Override + public FrameInstance visitFrame(final FrameInstance firstFrameInstance) { + RootCallTarget rt = (RootCallTarget) firstFrameInstance.getCallTarget(); + assert rt.getRootNode() instanceof Invokable; + Invokable rootNode = (Invokable) rt.getRootNode(); + SourceSection ss = null; + if (firstFrameInstance.getCallNode() != null) { + ss = firstFrameInstance.getCallNode().getSourceSection(); + } + if (ss == null) { + ss = topNode; + } + result[0] = new StackFrameDescription(ss, + firstFrameInstance.getFrame(FrameAccess.READ_ONLY), + rootNode, -1); + + return firstFrameInstance; + } + }); + firstFrame = result[0]; + } + + @Override + protected StackFrameDescription getFirstFrame() { + return firstFrame; + } + + @Override + protected StackFrameDescription getFrameBySource(final SourceSection sourceSection) { + return null; + } + } + } +} diff --git a/src/tools/debugger/session/BreakpointEnabling.java b/src/tools/debugger/breakpoints/BreakpointEnabling.java similarity index 97% rename from src/tools/debugger/session/BreakpointEnabling.java rename to src/tools/debugger/breakpoints/BreakpointEnabling.java index cd5d642b22..d663ea0c19 100644 --- a/src/tools/debugger/session/BreakpointEnabling.java +++ b/src/tools/debugger/breakpoints/BreakpointEnabling.java @@ -1,4 +1,4 @@ -package tools.debugger.session; +package tools.debugger.breakpoints; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.Truffle; diff --git a/src/tools/debugger/session/BreakpointInfo.java b/src/tools/debugger/breakpoints/BreakpointInfo.java similarity index 95% rename from src/tools/debugger/session/BreakpointInfo.java rename to src/tools/debugger/breakpoints/BreakpointInfo.java index deaa0b2a55..b510372ccb 100644 --- a/src/tools/debugger/session/BreakpointInfo.java +++ b/src/tools/debugger/breakpoints/BreakpointInfo.java @@ -1,4 +1,4 @@ -package tools.debugger.session; +package tools.debugger.breakpoints; import com.oracle.truffle.api.debug.Breakpoint; diff --git a/src/tools/debugger/session/Breakpoints.java b/src/tools/debugger/breakpoints/Breakpoints.java similarity index 91% rename from src/tools/debugger/session/Breakpoints.java rename to src/tools/debugger/breakpoints/Breakpoints.java index 0010546039..6b8149d517 100644 --- a/src/tools/debugger/session/Breakpoints.java +++ b/src/tools/debugger/breakpoints/Breakpoints.java @@ -1,4 +1,4 @@ -package tools.debugger.session; +package tools.debugger.breakpoints; import java.util.HashMap; import java.util.Map; @@ -60,10 +60,13 @@ public void prepareSteppingAfterNextRootNode(final Thread thread) { public synchronized void addOrUpdate(final LineBreakpoint bId) { Breakpoint bp = truffleBreakpoints.get(bId); if (bp == null) { - WebDebugger.log("LineBreakpoint: " + bId); + WebDebugger.log("[DEBUGGER] LineBreakpoint: " + bId + " Enabled: " + bId.isEnabled()); bp = Breakpoint.newBuilder(bId.getURI()).lineIs(bId.getLine()).build(); debuggerSession.install(bp); truffleBreakpoints.put(bId, bp); + } else { + WebDebugger.log( + "[DEBUGGER] Update LineBreakpoint: " + bId + " Enabled: " + bId.isEnabled()); } bp.setEnabled(bId.isEnabled()); } @@ -74,8 +77,11 @@ public synchronized void addOrUpdate(final SectionBreakpoint bId) { if (existingBP == null) { existingBP = new BreakpointEnabling(bId); breakpoints.put(loc, existingBP); + WebDebugger.log("[DEBUGGER] SectionBreakpoint: " + bId + " Enabled: " + bId.isEnabled()); } else { existingBP.setEnabled(bId.isEnabled()); + WebDebugger.log( + "[DEBUGGER] Update SectionBreakpoint: " + bId + " Enabled: " + bId.isEnabled()); } } @@ -101,7 +107,7 @@ private Breakpoint saveTruffleBasedBreakpoints(final SectionBreakpoint bId, final Class tag, final SuspendAnchor anchor) { Breakpoint bp = truffleBreakpoints.get(bId); if (bp == null) { - WebDebugger.log("SectionBreakpoint: " + bId); + WebDebugger.log("[DEBUGGER] SectionBreakpoint: " + bId); bp = Breakpoint.newBuilder(bId.getCoordinate().uri).lineIs(bId.getCoordinate().startLine) .columnIs(bId.getCoordinate().startColumn) .sectionLength(bId.getCoordinate().charLength) diff --git a/src/tools/debugger/session/LineBreakpoint.java b/src/tools/debugger/breakpoints/LineBreakpoint.java similarity index 97% rename from src/tools/debugger/session/LineBreakpoint.java rename to src/tools/debugger/breakpoints/LineBreakpoint.java index 09787ea906..3f464b4e0e 100644 --- a/src/tools/debugger/session/LineBreakpoint.java +++ b/src/tools/debugger/breakpoints/LineBreakpoint.java @@ -1,4 +1,4 @@ -package tools.debugger.session; +package tools.debugger.breakpoints; import java.net.URI; import java.util.Objects; diff --git a/src/tools/debugger/session/SectionBreakpoint.java b/src/tools/debugger/breakpoints/SectionBreakpoint.java similarity index 97% rename from src/tools/debugger/session/SectionBreakpoint.java rename to src/tools/debugger/breakpoints/SectionBreakpoint.java index 8952d9f33d..0b0799ace3 100644 --- a/src/tools/debugger/session/SectionBreakpoint.java +++ b/src/tools/debugger/breakpoints/SectionBreakpoint.java @@ -1,4 +1,4 @@ -package tools.debugger.session; +package tools.debugger.breakpoints; import java.util.Objects; diff --git a/src/tools/debugger/entities/BreakpointType.java b/src/tools/debugger/entities/BreakpointType.java index feecdd39f0..00fb54673b 100644 --- a/src/tools/debugger/entities/BreakpointType.java +++ b/src/tools/debugger/entities/BreakpointType.java @@ -16,8 +16,8 @@ import tools.concurrency.Tags.ReleaseLock; import tools.concurrency.Tags.WhenResolved; import tools.concurrency.Tags.WhenResolvedOnError; -import tools.debugger.session.Breakpoints; -import tools.debugger.session.SectionBreakpoint; +import tools.debugger.breakpoints.Breakpoints; +import tools.debugger.breakpoints.SectionBreakpoint; @SuppressWarnings({"unchecked", "rawtypes"}) diff --git a/src/tools/debugger/entities/Marker.java b/src/tools/debugger/entities/Marker.java index 1191b4e92a..d6d0cc9611 100644 --- a/src/tools/debugger/entities/Marker.java +++ b/src/tools/debugger/entities/Marker.java @@ -34,4 +34,6 @@ public class Marker { public static final byte PROMISE_MSG_SEND = 22; + public static final byte ACTOR_MSG_RECEIVE = 23; + } diff --git a/src/tools/debugger/entities/MessageReception.java b/src/tools/debugger/entities/MessageReception.java new file mode 100644 index 0000000000..07fce1dfbe --- /dev/null +++ b/src/tools/debugger/entities/MessageReception.java @@ -0,0 +1,25 @@ +package tools.debugger.entities; + +public enum MessageReception { + MESSAGE_RCV(Marker.ACTOR_MSG_RECEIVE, EntityType.ACTOR); + + private final byte id; + private final EntityType source; + + MessageReception(final byte id, final EntityType source) { + this.id = id; + this.source = source; + } + + public byte getId() { + return id; + } + + public EntityType getSource() { + return source; + } + + public int getSize() { + return 9; + } +} diff --git a/src/tools/debugger/entities/SendOp.java b/src/tools/debugger/entities/SendOp.java index 89f5577a2c..2e4ba9d903 100644 --- a/src/tools/debugger/entities/SendOp.java +++ b/src/tools/debugger/entities/SendOp.java @@ -1,5 +1,8 @@ package tools.debugger.entities; +import tools.TraceData; + + public enum SendOp { ACTOR_MSG(Marker.ACTOR_MSG_SEND, EntityType.ACT_MSG, EntityType.ACTOR), PROMISE_MSG(Marker.PROMISE_MSG_SEND, EntityType.ACT_MSG, EntityType.PROMISE), @@ -10,6 +13,10 @@ public enum SendOp { private final EntityType entity; private final EntityType target; + private static final int SYMBOL_ID_SIZE = 2; + private static final int RECEIVER_ACTOR_ID_SIZE = 8; + private static final int PROMISE_VALUE_SIZE_SIZE = 4; + SendOp(final byte id, final EntityType entity, final EntityType target) { this.id = id; this.entity = entity; @@ -29,6 +36,14 @@ public EntityType getTarget() { } public int getSize() { - return 17; + return 17 + RECEIVER_ACTOR_ID_SIZE + SYMBOL_ID_SIZE + TraceData.SOURCE_SECTION_SIZE; + } + + // Adds the promise value size and the number of bytes for an integer (4), which is also + // recorded + public int getSize(final byte[] promiseValue) { + int dataLength = promiseValue == null ? 0 : promiseValue.length; + return 17 + RECEIVER_ACTOR_ID_SIZE + SYMBOL_ID_SIZE + TraceData.SOURCE_SECTION_SIZE + + dataLength + PROMISE_VALUE_SIZE_SIZE; } } diff --git a/src/tools/debugger/entities/SteppingType.java b/src/tools/debugger/entities/SteppingType.java index 94cbc67594..902750c97e 100644 --- a/src/tools/debugger/entities/SteppingType.java +++ b/src/tools/debugger/entities/SteppingType.java @@ -196,6 +196,16 @@ public void process(final Suspension susp) { susp.getEvent().prepareContinue(); susp.getActivityThread().setSteppingStrategy(this); } + }, + + @SerializedName("stepToEndTurn") + STEP_TO_END_TURN("stepToEndTurn", "Step to End Turn", + Group.ACTOR_STEPPING, "msg-close", null, new ActivityType[] {ActivityType.ACTOR}) { + @Override + public void process(final Suspension susp) { + susp.getEvent().prepareContinue(); + susp.getActivityThread().setSteppingStrategy(this); + } }; public enum Group { diff --git a/src/tools/debugger/frontend/ApplicationThreadStack.java b/src/tools/debugger/frontend/ApplicationThreadStack.java index 195084c176..197cd98f40 100644 --- a/src/tools/debugger/frontend/ApplicationThreadStack.java +++ b/src/tools/debugger/frontend/ApplicationThreadStack.java @@ -1,20 +1,21 @@ package tools.debugger.frontend; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.*; -import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; import som.interpreter.LexicalScope.MethodScope; +import tools.debugger.asyncstacktraces.StackIterator; /** * Keeps information on the run-time stack of an application thread for * requests from the front-end. Is populated only on demand. */ -class ApplicationThreadStack { +public class ApplicationThreadStack { /** * Track scopes that contain variables as well as objects. @@ -24,30 +25,95 @@ class ApplicationThreadStack { final ArrayList scopesAndObjects; final HashMap scopesAndObjectsSet; - private final ArrayList stackFrames; - private final SuspendedEvent event; - private final Suspension suspension; + private final ArrayList stackFrames; + private final SuspendedEvent event; + private final Suspension suspension; - ApplicationThreadStack(final SuspendedEvent event, final Suspension suspension) { - this.event = event; + public static class StackFrame { + public final String name; + public final SourceSection section; + public final Frame frame; + public final boolean asyncOperation; + private final RootNode root; + public boolean fromMethodCache = false; + + private StackFrame(){ + asyncOperation = true; + name = "Not a Stack Frame"; + section = null; + frame = null; + root = null; + } + + public StackFrame(final String name, final RootNode root, final SourceSection section, + final Frame frame, final boolean asyncOperation) { + this(name,root,section,frame,asyncOperation,false); + } + + public StackFrame(final String name, final RootNode root, final SourceSection section, + final Frame frame, final boolean asyncOperation, final boolean fromMethodCache) { + this.name = name; + this.root = root; + this.section = section; + this.frame = frame; + this.asyncOperation = asyncOperation; + this.fromMethodCache = fromMethodCache; + } + + public RootNode getRootNode() { + return root; + } + + public boolean hasFrame() { + return frame != null; + } + } + + public static class ParallelStack extends StackFrame { + public List> parallelStacks; + + public ParallelStack(List> parallelStacks){ + this.parallelStacks = parallelStacks; + } + + } + + ApplicationThreadStack(final Suspension suspension) { + this.event = suspension.getEvent(); this.stackFrames = new ArrayList<>(); this.scopesAndObjects = new ArrayList<>(); this.scopesAndObjectsSet = new HashMap<>(); this.suspension = suspension; } - ArrayList get() { + ArrayList get() { if (stackFrames.isEmpty()) { - for (DebugStackFrame frame : event.getStackFrames()) { - stackFrames.add(frame); + StackIterator stack = + StackIterator.createSuspensionIterator(event.getStackFrames().iterator(), + suspension.getActivity().getId()); + + if (stack instanceof StackIterator.ShadowStackIterator.SuspensionShadowStackIterator) { + StackFrame topFrame = + ((StackIterator.ShadowStackIterator.SuspensionShadowStackIterator) stack).getTopFrame(); + // get top frame first + stackFrames.add(topFrame); } - assert !stackFrames.isEmpty() - : "We expect that there is always at least one stack frame"; + + while (stack.hasNext()) { + StackFrame next = stack.next(); + if (next != null) { // this validation is needed because at the moment + // ShadowStackIterator.next can return null values for null shadows + stackFrames.add(next); + } + } + assert !stackFrames.isEmpty() : "We expect that there is always at least one stack frame"; + } return stackFrames; } - long addScope(final Frame frame, final MethodScope lexicalScope) { + long addScope(final Frame frame, + final MethodScope lexicalScope) { scopesAndObjects.add(new RuntimeScope(frame, lexicalScope)); return getLastScopeOrVarId(); } diff --git a/src/tools/debugger/frontend/ApplicationThreadTask.java b/src/tools/debugger/frontend/ApplicationThreadTask.java index 2b16e20454..fe127eca2b 100644 --- a/src/tools/debugger/frontend/ApplicationThreadTask.java +++ b/src/tools/debugger/frontend/ApplicationThreadTask.java @@ -7,11 +7,11 @@ * A task that needs to be executed by the application thread while it is in a * {@link Suspension}. */ -abstract class ApplicationThreadTask { +public abstract class ApplicationThreadTask { /** * @return continue waiting on true, resume execution on false. */ - abstract boolean execute(); + protected abstract boolean execute(); static class Resume extends ApplicationThreadTask { @Override diff --git a/src/tools/debugger/frontend/RuntimeScope.java b/src/tools/debugger/frontend/RuntimeScope.java index b4cf184e41..607117f92a 100644 --- a/src/tools/debugger/frontend/RuntimeScope.java +++ b/src/tools/debugger/frontend/RuntimeScope.java @@ -10,10 +10,10 @@ public class RuntimeScope { private final Frame frame; private final MethodScope lexicalScope; - public RuntimeScope(final Frame frame, final MethodScope lexcialScope) { + public RuntimeScope(final Frame frame, final MethodScope lexicalScope) { this.frame = frame; - this.lexicalScope = lexcialScope; - assert frame.getFrameDescriptor() == lexcialScope.getFrameDescriptor(); + this.lexicalScope = lexicalScope; + assert frame.getFrameDescriptor() == lexicalScope.getFrameDescriptor(); } public Variable[] getVariables() { diff --git a/src/tools/debugger/frontend/Suspension.java b/src/tools/debugger/frontend/Suspension.java index d701cf4362..e85f328dcb 100644 --- a/src/tools/debugger/frontend/Suspension.java +++ b/src/tools/debugger/frontend/Suspension.java @@ -2,10 +2,12 @@ import java.util.ArrayList; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; -import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import som.interpreter.LexicalScope.MethodScope; import som.interpreter.actors.Actor; @@ -15,11 +17,14 @@ import som.interpreter.objectstorage.ObjectTransitionSafepoint; import som.primitives.ObjectPrims.HaltPrim; import som.vm.Activity; +import som.vmobjects.SBlock; +import som.vmobjects.SObjectWithClass; import tools.TraceData; import tools.concurrency.TracingActivityThread; import tools.debugger.FrontendConnector; import tools.debugger.entities.EntityType; import tools.debugger.entities.SteppingType; +import tools.debugger.frontend.ApplicationThreadStack.StackFrame; import tools.debugger.frontend.ApplicationThreadTask.Resume; import tools.debugger.frontend.ApplicationThreadTask.SendStackTrace; import tools.debugger.message.VariablesRequest.FilterType; @@ -53,10 +58,10 @@ public Suspension(final TracingActivityThread activityThread, public synchronized void update(final SuspendedEvent e) { this.suspendedEvent = e; - this.stack = new ApplicationThreadStack(e, this); + this.stack = new ApplicationThreadStack(this); } - public synchronized ArrayList getStackFrames() { + public synchronized ArrayList getStackFrames() { return stack.get(); } @@ -92,12 +97,11 @@ public long getGlobalId(final int localId) { private int getLocalId(final long globalId) { assert TraceData.getActivityIdFromGlobalValId( - globalId) == activityId - : "should be an id for current activity, otherwise, something is wrong"; + globalId) == activityId : "should be an id for current activity, otherwise, something is wrong"; return TraceData.valIdFromGlobal(globalId); } - public synchronized DebugStackFrame getFrame(final long globalId) { + public synchronized StackFrame getFrame(final long globalId) { return stack.get().get(getLocalId(globalId)); } @@ -132,7 +136,7 @@ public void sendVariables(final long varRef, final FrontendConnector frontend, frontend.sendVariables(varRef, requestId, this, filter, start, count); } - private void submitTask(final ApplicationThreadTask task) { + public void submitTask(final ApplicationThreadTask task) { try { tasks.put(task); } catch (InterruptedException e) { @@ -175,9 +179,48 @@ public void suspendWithoutObjectSafepoints() { if (resolver != null) { resolver.getPromise().enableHaltOnResolution(); } + } else if (activityThread.isStepping( + SteppingType.STEP_TO_END_TURN)) { + assert activity instanceof Actor; + EventualMessage turnMessage = EventualMessage.getCurrentExecutingMessage(); + String actorName = ""; + String turnName = ""; + + Object[] args = turnMessage.getArgs(); + if (args.length > 0 && args[0] instanceof SObjectWithClass) { // e.g. + // PromiseSendMessage, + // DirectMessage + final SObjectWithClass actorObject = (SObjectWithClass) args[0]; + actorName = actorObject.getSOMClass().getName().getString(); + } + if (args.length > 0 && args[0] instanceof SBlock) { // e.g. PromiseCallbackMessage + SBlock block = (SBlock) args[0]; + turnName = block.getMethod().getInvokable().getName(); + } + + if (turnName.isEmpty()) { + if (actorName.isEmpty()) { + turnName = turnMessage.getSelector().getString(); + } else { + turnName = + actorName.concat(">>#").concat(turnMessage.getSelector().getString()); + } + } + + Node suspendedNode = this.getEvent().getNode(); + ArrayList stackFrames = this.getStackFrames(); + ArrayList rootNodeFrames = new ArrayList<>(); + // get root nodes for the frames in the stack + for (StackFrame frame : stackFrames) { + rootNodeFrames.add(frame.getRootNode()); + } + + this.getEvent().getSession().prepareStepEndTurn(Thread.currentThread(), + suspendedNode, turnName, rootNodeFrames); } } - } catch (InterruptedException e) { /* Just continue waiting */ } + } catch (InterruptedException| IllegalMonitorStateException e) { + /* Just continue waiting */ } } synchronized (this) { suspendedEvent = null; diff --git a/src/tools/debugger/message/EvaluateExpressionRequest.java b/src/tools/debugger/message/EvaluateExpressionRequest.java new file mode 100644 index 0000000000..4486d8aa87 --- /dev/null +++ b/src/tools/debugger/message/EvaluateExpressionRequest.java @@ -0,0 +1,58 @@ +package tools.debugger.message; + +import com.oracle.truffle.api.debug.DebugValue; + +import org.java_websocket.WebSocket; +import som.primitives.ObjectPrims; +import tools.debugger.FrontendConnector; +import tools.debugger.frontend.ApplicationThreadTask; +import tools.debugger.frontend.Suspension; +import com.oracle.truffle.api.debug.DebugStackFrame; + +import java.util.Iterator; + + +public class EvaluateExpressionRequest extends Message.IncommingMessage { + + private class EvaluateExpressionTask extends ApplicationThreadTask { + + private final FrontendConnector frontend; + private final Suspension suspension; + private final int frameID; + private final String expression; + public Boolean executed = false; + public DebugValue resultValue; + + EvaluateExpressionTask(final FrontendConnector frontend, final Suspension suspension, + int frameID, String expression) { + this.frontend = frontend; + this.suspension = suspension; + this.frameID = frameID; + this.expression = expression; + + } + + @Override + protected boolean execute() { + // Iterator it + // =frontend.getSuspension(0).getEvent().getStackFrames().iterator(); + // DebugStackFrame frame = null; + // for(int i=0;i<=frameID;i++){ + // frame = it.next(); + // } + // resultValue = frame.eval("class _test = (||) \n ( \n public expression() = (^ "+ + // expression + ". \n ) \n )"); + // executed = true; + // return true; + return true; + } + } + + private String expression; + private int frameId; + + @Override + public void process(FrontendConnector connector, WebSocket conn) { + + } +} diff --git a/src/tools/debugger/message/InitializationResponse.java b/src/tools/debugger/message/InitializationResponse.java index 79ce5eafc7..c21d24b9f1 100644 --- a/src/tools/debugger/message/InitializationResponse.java +++ b/src/tools/debugger/message/InitializationResponse.java @@ -7,6 +7,7 @@ import tools.debugger.entities.DynamicScopeType; import tools.debugger.entities.EntityType; import tools.debugger.entities.Implementation; +import tools.debugger.entities.MessageReception; import tools.debugger.entities.PassiveEntityType; import tools.debugger.entities.ReceiveOp; import tools.debugger.entities.SendOp; @@ -214,6 +215,7 @@ private static final class ServerCapabilities { private final ParseData passiveEntityParseData; private final ParseData dynamicScopeParseData; private final ParseData sendReceiveParseData; + private final ParseData actorMessageReceiveData; private final ImplementationData[] implementationData; private final BreakpointData[] breakpointTypes; @@ -227,7 +229,7 @@ private ServerCapabilities(final EntityType[] supportedEntities, final ReceiveOp[] supportedReceiveOps, final BreakpointType[] supportedBreakpoints, final SteppingType[] supportedSteps, - final Implementation[] implData) { + final Implementation[] implData, final MessageReception[] msgReception) { activities = getDefinitions(supportedActivities); passiveEntities = getDefinitions(supportedPassiveEntities); dynamicScopes = getDefinitions(supportedDynamicScopes); @@ -247,6 +249,7 @@ private ServerCapabilities(final EntityType[] supportedEntities, sendReceiveParseData = new ParseData( supportedSendOps[0].getSize(), supportedReceiveOps[0].getSize()); + actorMessageReceiveData = new ParseData(msgReception[0].getSize(), 0); implementationData = getDefinitions(implData); @@ -271,10 +274,11 @@ public static InitializationResponse create(final EntityType[] supportedEntities final SendOp[] supportedSendOps, final ReceiveOp[] supportedReceiveOps, final BreakpointType[] supportedBreakpoints, - final SteppingType[] supportedSteps, final Implementation[] implData) { + final SteppingType[] supportedSteps, final Implementation[] implData, + final MessageReception[] msgReception) { return new InitializationResponse(new ServerCapabilities(supportedEntities, supportedActivities, supportedPassiveEntities, supportedDynamicScopes, supportedSendOps, supportedReceiveOps, supportedBreakpoints, - supportedSteps, implData)); + supportedSteps, implData, msgReception)); } } diff --git a/src/tools/debugger/message/InitializeConnection.java b/src/tools/debugger/message/InitializeConnection.java index 61ca8b5920..cd1619c655 100644 --- a/src/tools/debugger/message/InitializeConnection.java +++ b/src/tools/debugger/message/InitializeConnection.java @@ -3,8 +3,8 @@ import org.java_websocket.WebSocket; import tools.debugger.FrontendConnector; +import tools.debugger.breakpoints.BreakpointInfo; import tools.debugger.message.Message.IncommingMessage; -import tools.debugger.session.BreakpointInfo; public class InitializeConnection extends IncommingMessage { diff --git a/src/tools/debugger/message/PauseActorRequest.java b/src/tools/debugger/message/PauseActorRequest.java new file mode 100644 index 0000000000..342e3803ea --- /dev/null +++ b/src/tools/debugger/message/PauseActorRequest.java @@ -0,0 +1,29 @@ +package tools.debugger.message; + +import org.java_websocket.WebSocket; + +import som.interpreter.actors.Actor; +import tools.debugger.FrontendConnector; + + +public class PauseActorRequest extends Message.IncommingMessage { + private final long actorId; + + public PauseActorRequest() { + actorId = -1; + } + + public PauseActorRequest(long actorId) { + this.actorId = actorId; + } + + @Override + public void process(FrontendConnector connector, WebSocket conn) { + Actor actor = connector.getActorById(this.actorId); + assert actor != null : "Failed to get actor for activityId: " + this.actorId; + actor.setStepToNextTurn(true); + FrontendConnector.log("[DEBUGGER] Actor " + actor.getId() + + " will pause before processing the next message."); + connector.sendPauseActorResponse(this.actorId); + } +} diff --git a/src/tools/debugger/message/PauseActorResponse.java b/src/tools/debugger/message/PauseActorResponse.java new file mode 100644 index 0000000000..b2e5234b95 --- /dev/null +++ b/src/tools/debugger/message/PauseActorResponse.java @@ -0,0 +1,13 @@ +package tools.debugger.message; + +public class PauseActorResponse extends Message.OutgoingMessage { + private long actorId; + + public PauseActorResponse(long actorId) { + this.actorId = actorId; + } + + public static Message create(long pausedActorId) { + return new PauseActorResponse(pausedActorId); + } +} diff --git a/src/tools/debugger/message/ResumeActorResponse.java b/src/tools/debugger/message/ResumeActorResponse.java new file mode 100644 index 0000000000..a5d4447090 --- /dev/null +++ b/src/tools/debugger/message/ResumeActorResponse.java @@ -0,0 +1,13 @@ +package tools.debugger.message; + +public class ResumeActorResponse extends Message.OutgoingMessage { + private long actorId; + + public ResumeActorResponse(long actorId) { + this.actorId = actorId; + } + + public static Message create(long runningActorId) { + return new ResumeActorResponse(runningActorId); + } +} diff --git a/src/tools/debugger/message/ScopesResponse.java b/src/tools/debugger/message/ScopesResponse.java index 7af94e8fb3..2b78496d59 100644 --- a/src/tools/debugger/message/ScopesResponse.java +++ b/src/tools/debugger/message/ScopesResponse.java @@ -2,7 +2,6 @@ import java.util.ArrayList; -import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.nodes.RootNode; @@ -14,6 +13,7 @@ import som.interpreter.actors.ReceivedRootNode; import som.vmobjects.SBlock; import tools.TraceData; +import tools.debugger.frontend.ApplicationThreadStack.StackFrame; import tools.debugger.frontend.Suspension; import tools.debugger.message.Message.Response; @@ -32,7 +32,9 @@ private ScopesResponse(final long globalFrameId, final Scope[] scopes, } private static final class Scope { - /** Name of the scope such as 'Arguments', 'Locals'. */ + /** + * Name of the scope such as 'Arguments', 'Locals'. + */ private final String name; /** @@ -41,7 +43,9 @@ private static final class Scope { */ private final long variablesReference; - /** If true, the number of variables in this scope is large or expensive to retrieve. */ + /** + * If true, the number of variables in this scope is large or expensive to retrieve. + */ private final boolean expensive; private Scope(final String name, final long globalVarRef, @@ -68,27 +72,29 @@ private static void addScopes(final ArrayList scopes, public static ScopesResponse create(final long globalFrameId, final Suspension suspension, final int requestId) { - DebugStackFrame frame = suspension.getFrame(globalFrameId); + StackFrame frame = suspension.getFrame(globalFrameId); ArrayList scopes = new ArrayList<>(SMALL_INITIAL_SIZE); - Frame actualFrame = frame.getFrame(); - - RootNode invokable = frame.getRootNode(); - if (invokable instanceof Method) { - Method m = (Method) invokable; - MethodScope scope = m.getLexicalScope(); - long scopeId = suspension.addScope(actualFrame, scope); - scopes.add(new Scope("Locals", scopeId, false)); - - Object rcvr = SArguments.rcvr(actualFrame); - addScopes(scopes, scope, rcvr, suspension); - } else if (invokable instanceof ReceivedRootNode) { - // NOOP, no scopes here - assert false : "This should not be reached. This scope should never get an id"; - } else { - assert invokable instanceof Primitive : "Got a " + invokable.getClass().getSimpleName() + - " here. Means we need to add support"; + if (frame.hasFrame()) { + Frame actualFrame = frame.frame; + RootNode invokable = frame.getRootNode(); + if (invokable instanceof Method) { + Method m = (Method) invokable; + MethodScope scope = m.getLexicalScope(); + + long scopeId = suspension.addScope(actualFrame, scope); + scopes.add(new Scope("Locals", scopeId, false)); + Object rcvr = SArguments.rcvr(actualFrame); + addScopes(scopes, scope, rcvr, suspension); + } else if (invokable instanceof ReceivedRootNode) { + // NOOP, no scopes here + assert false : "This should not be reached. This scope should never get an id"; + } else { + assert invokable instanceof Primitive : "Got a " + invokable.getClass().getSimpleName() + + + " here. Means we need to add support"; + } } - return new ScopesResponse(globalFrameId, scopes.toArray(new Scope[0]), requestId); + } } diff --git a/src/tools/debugger/message/StackTraceResponse.java b/src/tools/debugger/message/StackTraceResponse.java index 0adc25e83f..c745ddbf45 100644 --- a/src/tools/debugger/message/StackTraceResponse.java +++ b/src/tools/debugger/message/StackTraceResponse.java @@ -1,14 +1,20 @@ package tools.debugger.message; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; -import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.source.SourceSection; +import som.interpreter.actors.Actor; import som.interpreter.actors.Actor.ExecutorRootNode; +import som.interpreter.actors.EventualMessage; import som.interpreter.actors.ReceivedRootNode; +import som.vm.VmSettings; import tools.TraceData; import tools.debugger.entities.EntityType; +import tools.debugger.frontend.ApplicationThreadStack; import tools.debugger.frontend.Suspension; import tools.debugger.message.Message.Response; @@ -21,21 +27,27 @@ public final class StackTraceResponse extends Response { // but that would make tracking more difficult private final byte[] concurrentEntityScopes; private final long activityId; + private long messageId; /** * Total number of frames available. */ private final int totalFrames; + private boolean asyncStack; + private StackTraceResponse(final long activityId, final StackFrame[] stackFrames, final int totalFrames, - final int requestId, final byte[] concurrentEntityScopes) { + final int requestId, final byte[] concurrentEntityScopes, final long messageId, + final boolean asyncStack) { super(requestId); assert TraceData.isWithinJSIntValueRange(activityId); this.activityId = activityId; this.stackFrames = stackFrames; this.totalFrames = totalFrames; this.concurrentEntityScopes = concurrentEntityScopes; + this.messageId = messageId; + this.asyncStack = asyncStack; boolean assertsOn = false; assert assertsOn = true; @@ -74,9 +86,14 @@ private static class StackFrame { /** An optional number of characters in the range. */ private final int length; + /** Indicates if the frame corresponds to an async operation. */ + private final boolean async; + + private List> parallelStacks; + StackFrame(final long globalId, final String name, final String sourceUri, final int line, final int column, final int endLine, - final int endColumn, final int length) { + final int endColumn, final int length, final boolean async) { assert TraceData.isWithinJSIntValueRange(globalId); this.id = globalId; this.name = name; @@ -86,10 +103,27 @@ private static class StackFrame { this.endLine = endLine; this.endColumn = endColumn; this.length = length; + this.async = async; + } + + StackFrame(final long globalId, final String name, final String sourceUri, + final int line, final int column, final int endLine, + final int endColumn, final int length, final boolean async, Suspension suspension,ApplicationThreadStack.ParallelStack parallelStackFrame){ + this(globalId,name,sourceUri,line,column,endLine,endColumn,length,async); + int count = 0; + this.parallelStacks = new LinkedList<>(); + for(List stackFrameList : parallelStackFrame.parallelStacks){ + List internalList = new LinkedList<>(); + for(ApplicationThreadStack.StackFrame stackFrame : stackFrameList){ + internalList.add(createFrame(suspension, count++,stackFrame)); + } + parallelStacks.add(internalList); + } } } - private static int getNumRootNodesToSkip(final ArrayList frames) { + private static int getNumRootNodesToSkip( + final ArrayList frames) { int skip = 0; int size = frames.size(); @@ -108,7 +142,7 @@ private static int getNumRootNodesToSkip(final ArrayList frames public static StackTraceResponse create(final int startFrame, final int levels, final Suspension suspension, final int requestId) { - ArrayList frames = suspension.getStackFrames(); + ArrayList frames = suspension.getStackFrames(); int skipFrames = suspension.getFrameSkipCount(); if (startFrame > skipFrames) { @@ -126,29 +160,45 @@ public static StackTraceResponse create(final int startFrame, final int levels, for (int i = 0; i < numFrames; i += 1) { int frameId = i + skipFrames; - assert !(frames.get( - frameId).getRootNode() instanceof ReceivedRootNode) - : "This should have been skipped in the code above"; + // TODO: remove the below assert once we are satisfied things work. because now we can + // have received root nodes in the stack trace + // assert !(frames.get( + // frameId).getRootNode() instanceof ReceivedRootNode) : "This should have been skipped + // in the + // code above"; StackFrame f = createFrame(suspension, frameId, frames.get(frameId)); arr[i] = f; } EntityType[] concEntityScopes = suspension.getCurrentEntityScopes(); + // determine the message id to which this trace corresponds + long messageId = -1; + + Actor actorCurrentMessageIsExecutionOn = + EventualMessage.getActorCurrentMessageIsExecutionOn(); + + if (actorCurrentMessageIsExecutionOn != null + && actorCurrentMessageIsExecutionOn.getId() == suspension.getActivity().getId()) { + EventualMessage message = EventualMessage.getCurrentExecutingMessage(); + messageId = message.getMessageId(); + } + return new StackTraceResponse(suspension.activityId, arr, frames.size(), - requestId, EntityType.getIds(concEntityScopes)); + requestId, EntityType.getIds(concEntityScopes), messageId, + VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE); } private static StackFrame createFrame(final Suspension suspension, - final int frameId, final DebugStackFrame frame) { + final int frameId, final ApplicationThreadStack.StackFrame frame) { long id = suspension.getGlobalId(frameId); - String name = frame.getName(); + String name = frame.name; if (name == null) { name = "vm (internal)"; } - SourceSection ss = frame.getSourceSection(); + SourceSection ss = frame.section; String sourceUri; int line; int column; @@ -170,6 +220,13 @@ private static StackFrame createFrame(final Suspension suspension, endColumn = 0; length = 0; } - return new StackFrame(id, name, sourceUri, line, column, endLine, endColumn, length); + + boolean async = frame.asyncOperation; + if (frame instanceof ApplicationThreadStack.ParallelStack){ + return new StackFrame(id, name, sourceUri, line, column, endLine, endColumn, length, + async,suspension,(ApplicationThreadStack.ParallelStack) frame); + } + return new StackFrame(id, name, sourceUri, line, column, endLine, endColumn, length, + async); } } diff --git a/src/tools/debugger/message/StepMessage.java b/src/tools/debugger/message/StepMessage.java index 55d4e247ef..0918a9bdc6 100644 --- a/src/tools/debugger/message/StepMessage.java +++ b/src/tools/debugger/message/StepMessage.java @@ -2,6 +2,7 @@ import org.java_websocket.WebSocket; +import som.interpreter.actors.Actor; import tools.debugger.FrontendConnector; import tools.debugger.entities.SteppingType; import tools.debugger.frontend.Suspension; @@ -23,8 +24,37 @@ public class StepMessage extends IncommingMessage { @Override public void process(final FrontendConnector connector, final WebSocket conn) { Suspension susp = connector.getSuspension(activityId); - assert susp != null : "Failed to get suspension for activityId: " + activityId; - step.process(susp); - susp.resume(); + + if (susp != null && susp.getEvent() != null) { + step.process(susp); + susp.resume(); + if (step != SteppingType.RESUME) { + FrontendConnector.log( + "[DEBUGGER] Executing " + step.name + " for actor " + activityId); + } + } else if (step == SteppingType.RESUME) { + // notify frontend the actor that can be resumed in the GUI because the actor is not + // suspended, + // this is needed for actors that are paused explicitly in the frontend, and they are not + // actually suspended yet (mailbox is empty) + Actor actor = connector.getActorById(this.activityId); + assert actor != null : "Failed to get actor for activityId: " + this.activityId; + // reset step next turn flag to false + actor.setStepToNextTurn(false); + } else { + FrontendConnector.log( + "[DEBUGGER]: Failed to get suspension for activityId: " + activityId); + } + + sendResumeMessage(connector); + } + + private void sendResumeMessage(FrontendConnector connector) { + if (step == SteppingType.RESUME + || step == SteppingType.RETURN_FROM_TURN_TO_PROMISE_RESOLUTION) { + // send resume message + connector.sendResumeActorResponse(this.activityId); + FrontendConnector.log("[DEBUGGER] Resuming actor " + activityId); + } } } diff --git a/src/tools/debugger/message/StoppedMessage.java b/src/tools/debugger/message/StoppedMessage.java index 4135dc3316..d5e94cca14 100644 --- a/src/tools/debugger/message/StoppedMessage.java +++ b/src/tools/debugger/message/StoppedMessage.java @@ -45,9 +45,12 @@ public static StoppedMessage create(final Suspension suspension) { reason = Reason.breakpoint; } + String suspendedPosition = suspension.getEvent().getSuspendAnchor().name(); + ActivityType type = suspension.getActivity().getType(); // TODO: look into additional details that can be provided as text - return new StoppedMessage(reason, suspension.activityId, type, ""); + // at the moment pass the suspendedPosition as text + return new StoppedMessage(reason, suspension.activityId, type, suspendedPosition); } } diff --git a/src/tools/debugger/message/UpdateBreakpoint.java b/src/tools/debugger/message/UpdateBreakpoint.java index f9d9b47e5b..1aafe4c689 100644 --- a/src/tools/debugger/message/UpdateBreakpoint.java +++ b/src/tools/debugger/message/UpdateBreakpoint.java @@ -3,8 +3,8 @@ import org.java_websocket.WebSocket; import tools.debugger.FrontendConnector; +import tools.debugger.breakpoints.BreakpointInfo; import tools.debugger.message.Message.IncommingMessage; -import tools.debugger.session.BreakpointInfo; public class UpdateBreakpoint extends IncommingMessage { diff --git a/src/tools/debugger/nodes/BreakpointNode.java b/src/tools/debugger/nodes/BreakpointNode.java index 22babb2728..4bec212134 100644 --- a/src/tools/debugger/nodes/BreakpointNode.java +++ b/src/tools/debugger/nodes/BreakpointNode.java @@ -4,7 +4,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; -import tools.debugger.session.BreakpointEnabling; +import tools.debugger.breakpoints.BreakpointEnabling; /** diff --git a/src/tools/dym/DynamicMetrics.java b/src/tools/dym/DynamicMetrics.java index 327461c3c8..f5478be12a 100644 --- a/src/tools/dym/DynamicMetrics.java +++ b/src/tools/dym/DynamicMetrics.java @@ -208,8 +208,7 @@ public DynamicMetrics() { assert "DefaultTruffleRuntime".equals( Truffle.getRuntime().getClass() - .getSimpleName()) - : "To get metrics for the lexical, unoptimized behavior, please run this tool without Graal"; + .getSimpleName()) : "To get metrics for the lexical, unoptimized behavior, please run this tool without Graal"; } public void enterMethod() { @@ -441,8 +440,7 @@ private void addLoopBodyInstrumentation( instrumenter.attachExecutionEventFactory(filters.build(), (final EventContext ctx) -> { ExecutionEventNode parent = ctx.findDirectParentEventNode(loopProfileFactory); - assert parent != null - : "Direct parent does not seem to be set up properly with event node and/or wrapping"; + assert parent != null : "Direct parent does not seem to be set up properly with event node and/or wrapping"; LoopProfilingNode p = (LoopProfilingNode) parent; return new LoopIterationReportNode(p.getProfile()); }); diff --git a/src/tools/dym/nodes/OperationProfilingNode.java b/src/tools/dym/nodes/OperationProfilingNode.java index cb340e91ef..e4f7d0491b 100644 --- a/src/tools/dym/nodes/OperationProfilingNode.java +++ b/src/tools/dym/nodes/OperationProfilingNode.java @@ -55,8 +55,7 @@ protected void onReturnExceptional(final VirtualFrame frame, final Throwable e) public int registerSubexpressionAndGetIdx(final Node subExpr) { int idx = getChildIdx(subExpr); - assert idx >= 0 - : "Subexpression was not found. Something seems to be wrong with the instrumentation."; + assert idx >= 0 : "Subexpression was not found. Something seems to be wrong with the instrumentation."; return idx + 1; // + 1 is used to represent the index of the storage array used to hold the // result. Return value is at 0 index. } diff --git a/src/tools/dym/profiles/Arguments.java b/src/tools/dym/profiles/Arguments.java index a1ae60ab5e..c7c5adbe8b 100644 --- a/src/tools/dym/profiles/Arguments.java +++ b/src/tools/dym/profiles/Arguments.java @@ -8,6 +8,7 @@ import som.interpreter.Types; import som.interpreter.objectstorage.ClassFactory; +import som.vm.VmSettings; public final class Arguments { @@ -19,8 +20,14 @@ public final class Arguments { private final ClassFactory[] argSomTypes; Arguments(final Object[] arguments) { - this.argJavaTypes = getJavaTypes(arguments); - this.argSomTypes = getSomTypes(arguments); + Object[] argsToProfile; + if (VmSettings.ACTOR_ASYNC_STACK_TRACE_STRUCTURE) { + argsToProfile = Arrays.copyOf(arguments, arguments.length - 1); + } else { + argsToProfile = arguments; + } + this.argJavaTypes = getJavaTypes(argsToProfile); + this.argSomTypes = getSomTypes(argsToProfile); } private static Class[] getJavaTypes(final Object[] args) { @@ -87,14 +94,22 @@ public JSONObjectBuilder toJson() { JSONArrayBuilder javaTypes = JSONHelper.array(); for (Class c : argJavaTypes) { - javaTypes.add(c.getSimpleName()); + if (c == null) { + javaTypes.add((String) null); + } else { + javaTypes.add(c.getSimpleName()); + } } result.add("javaTypes", javaTypes); JSONArrayBuilder somTypes = JSONHelper.array(); for (ClassFactory c : argSomTypes) { - somTypes.add(c.getClassName().getString()); + if (c == null) { + somTypes.add((String) null); + } else { + somTypes.add(c.getClassName().getString()); + } } result.add("somTypes", somTypes); return result; diff --git a/src/tools/parser/KomposTraceParser.java b/src/tools/parser/KomposTraceParser.java index 0be1973a6a..a477afe1f5 100644 --- a/src/tools/parser/KomposTraceParser.java +++ b/src/tools/parser/KomposTraceParser.java @@ -126,6 +126,9 @@ private TraceRecords[] createParseTable() { @SuppressWarnings({"resource", "unused"}) private void parse(final String path) { + assert !VmSettings.KOMPOS_TRACING : "The Trace Parser is currently not supporting " + + "all Kompos features, for instance the value as part of the SendOperation"; + File traceFile = new File(path); HashMap openTurns = new HashMap<>(); diff --git a/src/tools/snapshot/nodes/MessageSerializationNode.java b/src/tools/snapshot/nodes/MessageSerializationNode.java index 4549722359..b68d25c36e 100644 --- a/src/tools/snapshot/nodes/MessageSerializationNode.java +++ b/src/tools/snapshot/nodes/MessageSerializationNode.java @@ -5,6 +5,7 @@ import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.Specialization; +import som.interpreter.SArguments; import som.interpreter.SomLanguage; import som.interpreter.Types; import som.interpreter.actors.Actor; @@ -410,7 +411,7 @@ private PromiseSendMessage deserializeDelivered(final SSymbol selector, final Ac pmf.setMessage(psm); } psm.resolve(value, EventualMessage.getActorCurrentMessageIsExecutionOn(), - finalSender); + finalSender, SArguments.instantiateTopShadowStackEntry(this)); return psm; } diff --git a/src/tools/snapshot/nodes/ObjectSerializationNodes.java b/src/tools/snapshot/nodes/ObjectSerializationNodes.java index eea8f33d0b..e73a8bcf2f 100644 --- a/src/tools/snapshot/nodes/ObjectSerializationNodes.java +++ b/src/tools/snapshot/nodes/ObjectSerializationNodes.java @@ -186,7 +186,7 @@ public void serialize(final SObject so, final SnapshotBuffer sb) { protected final CachedSerializationNode[] getSerializers(final SObject o) { CachedSerializationNode[] nodes = new CachedSerializationNode[fieldCnt]; for (int i = 0; i < fieldCnt; i++) { - Object value = fieldReads[i].read(o); + Object value = fieldReads[i].read(null, o); nodes[i] = CachedSerializationNodeFactory.create(value); } return nodes; @@ -201,7 +201,7 @@ public void doCached(final SObject o, final SnapshotBuffer sb) { } for (int i = 0; i < fieldCnt; i++) { - Object value = fieldReads[i].read(o); + Object value = fieldReads[i].read(null, o); // TODO type profiles could be an optimization (separate profile for each slot) // TODO optimize, maybe it is better to add an integer to the objects (indicating their // offset) rather than using a map. diff --git a/tests/java/debugger/JsonTests.java b/tests/java/debugger/JsonTests.java index b16e93814f..188a8316b3 100644 --- a/tests/java/debugger/JsonTests.java +++ b/tests/java/debugger/JsonTests.java @@ -17,11 +17,15 @@ import bd.source.FullSourceCoordinate; import bd.source.SourceCoordinate; import tools.debugger.RuntimeReflectionRegistration; +import tools.debugger.breakpoints.BreakpointInfo; +import tools.debugger.breakpoints.LineBreakpoint; +import tools.debugger.breakpoints.SectionBreakpoint; import tools.debugger.entities.ActivityType; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.DynamicScopeType; import tools.debugger.entities.EntityType; import tools.debugger.entities.Implementation; +import tools.debugger.entities.MessageReception; import tools.debugger.entities.PassiveEntityType; import tools.debugger.entities.ReceiveOp; import tools.debugger.entities.SendOp; @@ -31,9 +35,6 @@ import tools.debugger.message.Message.IncommingMessage; import tools.debugger.message.Message.OutgoingMessage; import tools.debugger.message.UpdateBreakpoint; -import tools.debugger.session.BreakpointInfo; -import tools.debugger.session.LineBreakpoint; -import tools.debugger.session.SectionBreakpoint; public class JsonTests { @@ -192,7 +193,8 @@ public void initializeResponseSerialize() { String result = gson.toJson(InitializationResponse.create( EntityType.values(), ActivityType.values(), PassiveEntityType.values(), DynamicScopeType.values(), SendOp.values(), ReceiveOp.values(), - BreakpointType.values(), SteppingType.values(), Implementation.values()), + BreakpointType.values(), SteppingType.values(), Implementation.values(), + MessageReception.values()), OutgoingMessage.class); // This test is only doing a very basic sanity check assertTrue(1000 < result.length()); diff --git a/tests/java/debugger/ReflectionRegistrationTests.java b/tests/java/debugger/ReflectionRegistrationTests.java index ce2bffa9bd..257c75fe53 100644 --- a/tests/java/debugger/ReflectionRegistrationTests.java +++ b/tests/java/debugger/ReflectionRegistrationTests.java @@ -17,6 +17,6 @@ public void runtimeRegistration() { rrr.beforeAnalysis(null); HashSet> registeredClasses = rrr.getRegisteredClasses(); - assertEquals(56, registeredClasses.size()); + assertEquals(60, registeredClasses.size()); } } diff --git a/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java b/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java deleted file mode 100644 index e9413f9b59..0000000000 --- a/tests/java/som/interpreter/objectstorage/SafepointPhaserTest.java +++ /dev/null @@ -1,186 +0,0 @@ -package som.interpreter.objectstorage; - -import static org.junit.Assert.assertEquals; - -import java.util.concurrent.BrokenBarrierException; -import java.util.concurrent.CyclicBarrier; - -import org.graalvm.collections.EconomicSet; -import org.junit.Test; - -import som.compiler.AccessModifier; -import som.compiler.MixinDefinition.SlotDefinition; -import som.tests.ParallelHelper; -import som.vmobjects.SClass; -import som.vmobjects.SObject.SMutableObject; - - -public class SafepointPhaserTest { - - private final SClass instanceClass; - private final ClassFactory factory; - private final ObjectLayout layout; - - public SafepointPhaserTest() { - SlotDefinition slotDef = new SlotDefinition(null, AccessModifier.PUBLIC, false, null); - EconomicSet instanceSlots = EconomicSet.create(); - instanceSlots.add(slotDef); - - instanceClass = new SClass(null); - factory = new ClassFactory(null, null, instanceSlots, null, false, false, false, null, - false, null, null); - - instanceClass.initializeStructure(null, null, null, false, false, false, factory); - layout = factory.getInstanceLayout(); - } - - @Test - public void testThreadsRegisterAndUnregister() throws InterruptedException { - CyclicBarrier barrier = new CyclicBarrier(ParallelHelper.getNumberOfThreads()); - - SafepointPhaser phaser = new SafepointPhaser(ObjectTransitionSafepoint.INSTANCE); - ParallelHelper.executeNTimesInParallel((final int id) -> { - - phaser.register(); - - try { - barrier.await(); - } catch (InterruptedException | BrokenBarrierException e) { - throw new RuntimeException(e); - } - - phaser.arriveAndDeregister(); - return null; - }); - - assertEquals(phaser.getPhase(), 2); - } - - @Test - public void testThreadsRegisterTriggerSafepointAndUnregister() throws InterruptedException { - CyclicBarrier barrier = new CyclicBarrier(ParallelHelper.getNumberOfThreads()); - - ObjectTransitionSafepoint.reset(); - - ParallelHelper.executeNTimesInParallel((final int id) -> { - - try { - ObjectTransitionSafepoint.INSTANCE.register(); - - try { - barrier.await(); - } catch (InterruptedException | BrokenBarrierException e) { - throw new RuntimeException(e); - } - - ObjectTransitionSafepoint.INSTANCE.transitionObject( - new SMutableObject(instanceClass, factory, layout)); - } finally { - ObjectTransitionSafepoint.INSTANCE.unregister(); - } - return null; - }); - } - - private void transitionObject() { - for (int i = 0; i < 1181; i += 1) { - ObjectTransitionSafepoint.INSTANCE.transitionObject( - new SMutableObject(instanceClass, factory, layout)); - } - } - - @Test - public void testSafepointStorm() throws InterruptedException { - ObjectTransitionSafepoint.reset(); - ParallelHelper.executeNTimesInParallel((final int id) -> { - - try { - ObjectTransitionSafepoint.INSTANCE.register(); - - for (int i = 0; i < 1181; i += 1) { - transitionObject(); - } - } finally { - ObjectTransitionSafepoint.INSTANCE.unregister(); - } - return null; - }, 240); - } - - @Test - public void testSingleSafepointStorm() throws InterruptedException { - ObjectTransitionSafepoint.reset(); - ParallelHelper.executeNTimesInParallel((final int id) -> { - try { - ObjectTransitionSafepoint.INSTANCE.register(); - - for (int i = 0; i < 100_000; i += 1) { - if (id == 0) { - ObjectTransitionSafepoint.INSTANCE.transitionObject( - new SMutableObject(instanceClass, factory, layout)); - } else { - ObjectTransitionSafepoint.INSTANCE.checkAndPerformSafepoint(); - } - } - } finally { - ObjectTransitionSafepoint.INSTANCE.unregister(); - } - return null; - }, 240); - } - - @Test - public void testSafepointRegisterStorm() throws InterruptedException { - ObjectTransitionSafepoint.reset(); - ParallelHelper.executeNTimesInParallel((final int id) -> { - for (int i = 0; i < 100_000; i += 1) { - try { - ObjectTransitionSafepoint.INSTANCE.register(); - - ObjectTransitionSafepoint.INSTANCE.transitionObject( - new SMutableObject(instanceClass, factory, layout)); - } finally { - ObjectTransitionSafepoint.INSTANCE.unregister(); - } - } - return null; - }, 120); - } - - @Test - public void testSingleSafepointRegisterStorm() throws InterruptedException { - ObjectTransitionSafepoint.reset(); - ParallelHelper.executeNTimesInParallel((final int id) -> { - for (int i = 0; i < 100_000; i += 1) { - try { - ObjectTransitionSafepoint.INSTANCE.register(); - - if (id == 0) { - ObjectTransitionSafepoint.INSTANCE.transitionObject( - new SMutableObject(instanceClass, factory, layout)); - } else { - ObjectTransitionSafepoint.INSTANCE.checkAndPerformSafepoint(); - } - } finally { - ObjectTransitionSafepoint.INSTANCE.unregister(); - } - } - return null; - }, 60); - } - - @Test - public void testRegisterStorm() throws InterruptedException { - ObjectTransitionSafepoint.reset(); - ParallelHelper.executeNTimesInParallel((final int id) -> { - for (int i = 0; i < 100_000; i += 1) { - try { - ObjectTransitionSafepoint.INSTANCE.register(); - } finally { - ObjectTransitionSafepoint.INSTANCE.unregister(); - } - } - return null; - }, 60); - } -} diff --git a/tests/replay/test.sh b/tests/replay/test.sh index 8193406e4f..faf20120cf 100755 --- a/tests/replay/test.sh +++ b/tests/replay/test.sh @@ -8,7 +8,8 @@ function cleanup() curl --upload-file ./traces.tar.bz2 https://transfer.sh/traces.tar.bz2 } -trap cleanup EXIT +echo Upload of Traces on failure is currently disabled. +# trap cleanup EXIT if [ "$1" = "1" ] then diff --git a/tools/kompos/package.json b/tools/kompos/package.json index 22c3a7f3db..aadd80a2c4 100644 --- a/tools/kompos/package.json +++ b/tools/kompos/package.json @@ -14,32 +14,29 @@ "type": "git", "url": "https://github.com/smarr/SOMns.git" }, - "engines": { - "node": "13.11.1" - }, "dependencies": { "bootstrap": "4.4.1", "popper.js": "1.16.1", "d3": "^3.5.17", "font-awesome": "4.7.0", - "jquery": "3.5.0", + "jquery": "3.6.1", "node-define": "0.1.1", "requirejs": "2.3.6", - "ws": "7.2.3", + "ws": "8.9.0", "zenscroll": "4.0.2" }, "devDependencies": { "@types/bootstrap": "4.3.2", - "@types/chai": "4.2.11", + "@types/chai": "4.3.3", "@types/d3": "3.5.36", - "@types/jquery": "3.3.34", - "@types/mocha": "5.2.7", - "@types/node": "13.11.1", - "@types/ws": "7.2.4", - "chai": "4.2.0", - "mocha": "7.1.1", - "tslint": "6.1.1", - "typescript": "3.8.3", + "@types/jquery": "3.5.14", + "@types/mocha": "10.0.0", + "@types/node": "18.11.0", + "@types/ws": "8.5.3", + "chai": "4.3.6", + "mocha": "10.1.0", + "tslint": "6.1.3", + "typescript": "4.8.4", "typescript-formatter": "7.2.2" }, "scripts": { diff --git a/tools/kompos/src/messages.ts b/tools/kompos/src/messages.ts index 5c6694ed57..4400b06b01 100644 --- a/tools/kompos/src/messages.ts +++ b/tools/kompos/src/messages.ts @@ -223,6 +223,7 @@ export interface ServerCapabilities { passiveEntityParseData: ParseDef; dynamicScopeParseData: ParseDef; sendReceiveParseData: ParseDef; + actorMessageReceiveData: ParseDef; implementationData: ImplData[]; @@ -375,4 +376,3 @@ export interface Variable { /** The number of indexed child variables. */ indexedVariables: number; } - diff --git a/tools/kompos/src/trace-parser.ts b/tools/kompos/src/trace-parser.ts index 91a335588c..f59b4ad618 100644 --- a/tools/kompos/src/trace-parser.ts +++ b/tools/kompos/src/trace-parser.ts @@ -17,6 +17,7 @@ enum TraceRecords { PassiveEntityCompletion, SendOp, ReceiveOp, + MessageReceiveOp, ImplThread, ImplThreadCurrentActivity } @@ -25,6 +26,7 @@ const SOURCE_SECTION_SIZE = 8; const IMPL_THREAD_MARKER = 20; const IMPL_THREAD_CURRENT_ACTIVITY_MARKER = 21; +const ACTOR_MSG_RECEIVE_MARKER = 23; const RECORD_SIZE = { ActivityCreation: 11 + SOURCE_SECTION_SIZE, @@ -33,8 +35,9 @@ const RECORD_SIZE = { DynamicScopeEnd: 1, PassiveEntityCreation: 9 + SOURCE_SECTION_SIZE, PassiveEntityCompletion: undefined, - SendOp: 17, + SendOp: 17 + 8 /* rcvr actor id */ + 2 /* symbol id */ + SOURCE_SECTION_SIZE, ReceiveOp: 9, + MessageReceiveOp: 9, ImplThread: 9, ImplThreadCurrentActivity: 13 }; @@ -102,6 +105,7 @@ export class TraceParser { console.assert(this.metaModel.serverCapabilities.dynamicScopeParseData.completionSize === RECORD_SIZE.DynamicScopeEnd); console.assert(this.metaModel.serverCapabilities.sendReceiveParseData.creationSize === RECORD_SIZE.SendOp); console.assert(this.metaModel.serverCapabilities.sendReceiveParseData.completionSize === RECORD_SIZE.ReceiveOp); + console.assert(this.metaModel.serverCapabilities.actorMessageReceiveData.creationSize === RECORD_SIZE.MessageReceiveOp); console.assert(this.metaModel.serverCapabilities.implementationData[0].marker === IMPL_THREAD_MARKER); console.assert(this.metaModel.serverCapabilities.implementationData[0].size === RECORD_SIZE.ImplThread); @@ -110,6 +114,11 @@ export class TraceParser { this.parseTable[IMPL_THREAD_MARKER] = TraceRecords.ImplThread; this.parseTable[IMPL_THREAD_CURRENT_ACTIVITY_MARKER] = TraceRecords.ImplThreadCurrentActivity; + + // STEFAN: this was added by Carmen and doesn't really fit the bill, so, handled manually + this.parseTable[ACTOR_MSG_RECEIVE_MARKER] = TraceRecords.MessageReceiveOp; + + console.assert(Object.keys(this.metaModel.serverCapabilities).length === 13); } /** Read a long within JS int range */ @@ -137,8 +146,6 @@ export class TraceParser { this.execData.addRawActivity(new RawActivity( this.typeCreation[marker], activityId, symbolId, sourceSection, currentActivityId, currentScopeId)); - - return i + RECORD_SIZE.ActivityCreation; } private readScopeStart(i: number, data: DataView, @@ -150,8 +157,6 @@ export class TraceParser { this.execData.addRawScope(new RawScope( this.typeCreation[marker], id, source, currentActivityId, currentScopeId)); - - return i + RECORD_SIZE.DynamicScopeStart; } private readEntityCreation(i: number, data: DataView, @@ -163,21 +168,25 @@ export class TraceParser { this.execData.addRawPassiveEntity(new RawPassiveEntity( this.typeCreation[marker], id, source, currentActivityId, currentScopeId)); - - return i + RECORD_SIZE.PassiveEntityCreation; } private readSendOp(i: number, data: DataView, currentActivityId: number, currentScopeId: number) { const marker = data.getUint8(i); const entityId = this.readLong(data, i + 1); - const targetId = this.readLong(data, i + 9); + const targetId = this.readLong(data, i + 1 + 8); + // const targetActorId = this.readLong(data, i + 1 + 8 + 8); + // const symbolId = this.readShort(data, i + 1 + 8 + 8 + 8) + // const source section info 4x 2 byte at i + 1 + 8 + 8 + 8 + 2 + + const valueLength = data.getInt32(i + 1 + 8 + 8 + 8 + 2 + 8, true); this.execData.addRawSendOp(new RawSendOp( this.sendOps[marker], entityId, targetId, currentActivityId, currentScopeId)); - return i + RECORD_SIZE.SendOp; + console.assert(valueLength + 4 >= 0); + return valueLength + 4 /* 4 byte for the length */ } private readReceiveOp(i: number, data: DataView, @@ -188,8 +197,6 @@ export class TraceParser { this.execData.addRawReceiveOp(new RawReceiveOp( this.receiveOps[marker], sourceId, currentActivityId, currentScopeId)); - - return i + RECORD_SIZE.ReceiveOp; } public parseTrace(data: DataView) { @@ -207,29 +214,34 @@ export class TraceParser { const marker = data.getUint8(i); switch (this.parseTable[marker]) { case TraceRecords.ActivityCreation: - i = this.readActivityCreation(i, data, currentActivityId, currentScopeId); + this.readActivityCreation(i, data, currentActivityId, currentScopeId); + i += RECORD_SIZE.ActivityCreation; break; case TraceRecords.ActivityCompletion: this.execData.completeActivity(currentActivityId); i += RECORD_SIZE.ActivityCompletion; break; case TraceRecords.DynamicScopeStart: - i = this.readScopeStart(i, data, currentActivityId, currentScopeId); + this.readScopeStart(i, data, currentActivityId, currentScopeId); + i += RECORD_SIZE.DynamicScopeStart; break; case TraceRecords.DynamicScopeEnd: this.execData.endScope(currentScopeId); i += RECORD_SIZE.DynamicScopeEnd; break; case TraceRecords.PassiveEntityCreation: - i = this.readEntityCreation(i, data, currentActivityId, currentScopeId); + this.readEntityCreation(i, data, currentActivityId, currentScopeId); + i += RECORD_SIZE.PassiveEntityCreation; break; case TraceRecords.PassiveEntityCompletion: throw new Error("Should never be reached. Is not generated by Trace Buffer."); case TraceRecords.SendOp: - i = this.readSendOp(i, data, currentActivityId, currentScopeId); + const dataLength = this.readSendOp(i, data, currentActivityId, currentScopeId); + i += RECORD_SIZE.SendOp + dataLength; break; case TraceRecords.ReceiveOp: - i = this.readReceiveOp(i, data, currentActivityId, currentScopeId); + this.readReceiveOp(i, data, currentActivityId, currentScopeId); + i += RECORD_SIZE.ReceiveOp; break; case TraceRecords.ImplThread: { /* currentImplThreadId = */ this.readLong(data, i + 1); @@ -242,6 +254,10 @@ export class TraceParser { i += RECORD_SIZE.ImplThreadCurrentActivity; break; } + case TraceRecords.MessageReceiveOp: { + i += RECORD_SIZE.MessageReceiveOp; + break; + } default: throw new Error("Unexpected marker in trace: " + marker + " prev: " + prevMarker); } diff --git a/tools/kompos/tests/basic-protocol.ts b/tools/kompos/tests/basic-protocol.ts index fe29e83f0c..614098f574 100644 --- a/tools/kompos/tests/basic-protocol.ts +++ b/tools/kompos/tests/basic-protocol.ts @@ -185,7 +185,7 @@ describe("Basic Protocol", function() { breakpoint: createSectionBreakpointData(PING_PONG_URI, 41, 21, 17, BT.PROMISE_RESOLUTION, true), stackLength: 1, topMethod: "Thing>>#println", - line: 75 + line: 77 }, { test: "on resolved explicit promise, accept promise resolver breakpoint on resolved explicit promise, and halt on expected source section", @@ -222,7 +222,7 @@ describe("Basic Protocol", function() { breakpoint: createSectionBreakpointData(PING_PONG_URI, 95, 16, 3, BT.PROMISE_RESOLUTION, true), stackLength: 1, topMethod: "Thing>>#println", - line: 75 + line: 77 }, { test: "for chained resolution, accept promise resolution breakpoint for chained resolution, and halt on expected source section", @@ -243,21 +243,21 @@ describe("Basic Protocol", function() { breakpoint: createSectionBreakpointData(PING_PONG_URI, 27, 17, 32, BT.PROMISE_RESOLUTION, true), stackLength: 1, topMethod: "Thing>>#println", - line: 75 + line: 77 }, { test: "onError, accept promise resolution breakpoint onError, and halt on expected source section", breakpoint: createSectionBreakpointData(PING_PONG_URI, 78, 18, 50, BT.PROMISE_RESOLUTION, true), stackLength: 1, topMethod: "Thing>>#println", - line: 75 + line: 77 }, { test: "whenResolvedOnError, accept promise resolution breakpoint on whenResolvedOnError, and halt on expected source section", breakpoint: createSectionBreakpointData(PING_PONG_URI, 34, 17, 91, BT.PROMISE_RESOLUTION, true), stackLength: 1, topMethod: "Thing>>#println", - line: 75 + line: 77 }] }; diff --git a/tools/kompos/tests/test-setup.ts b/tools/kompos/tests/test-setup.ts index 05b8234ed5..5b42ebedbb 100644 --- a/tools/kompos/tests/test-setup.ts +++ b/tools/kompos/tests/test-setup.ts @@ -157,7 +157,7 @@ export class TestConnection extends VmConnection { return promise; } - public close(done: MochaDone) { + public close(done) { if (!this.closed) { this.closed = true; this.disconnect(); @@ -184,7 +184,7 @@ export class ControllerWithInitialBreakpoints extends Controller { export function execSom(extraArgs: string[]): SpawnSyncReturns { const args = ["-G", "-t1", "-dnu", "tests/pingpong.ns"].concat(extraArgs); - return spawnSync(SOM, args); + return spawnSync(SOM, args, { encoding: "utf-8" }); } export class HandleStoppedAndGetStackTrace extends ControllerWithInitialBreakpoints {